mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
chore(web): remove the Ralph wizard and its plan-generation UI
The wizard's only entry point was the "Start Ralph Loop" button on the start screen, removed in 1.50.0, so none of this could be opened or triggered from the app any more: - ralph-wizard.js (the wizard, plan generation flow and the floating plan subagent windows) and the #ralphWizardModal markup; - the "Plan Wizard Agents" section of the monitor panel with its cancel button, and the five plan:* SSE handlers that only fed these surfaces; - the wizard branches in the subagent window code: connection lines to the wizard and to plan agents, window placement beside the wizard, and the wizard cleanup on reconnect. The ordinary path (agent window to its parent tab) is unchanged, only un-nested; - the wizard's state on the app object and its z-index constant; - its styles: 67 classes, three keyframes and a second identical @keyframes spin. What stays: the Ralph panel, the Ralph tab in session options, the plan routes (/api/generate-plan, /api/generate-plan-detailed, /api/cancel-plan-generation, the ralph-wizard file routes) and the plan:* SSE events, which are part of the public API. A client that starts plan generation over the API still gets its events; the page no longer draws anything for them. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
f1b2a2ab75
commit
259cabd738
17 files changed
+43
-2467
No files matched your search
+1
-1
@@ -94,6 +94,6 @@ Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. L
|
||||
|
||||
**SSE staleness watchdog** (`computeSseStale()` in constants.js, `_checkSseStale()` + a 5s interval in app.js): an `EventSource` can stop delivering without erroring, so the client forces a reconnect when nothing arrives. ⚠️ The server keepalive must stay the named `sse:heartbeat` event (`cleanupDeadClients()`, sse-stream-manager.ts), never an SSE comment, which `EventSource` cannot observe; its no-op client listener must stay registered. ⚠️ Judge staleness only while `connected` and online (the loop breaker). ⚠️ The liveness stamp lives inside `addListener`. ⚠️ Clear the interval only at the top of `connectSSE()`, or intervals stack. ⚠️ The watchdog cannot see an attempt that never OPENS (no response headers: no `open`, no `error`, never `connected`), so each attempt is bounded by `SSE_CONNECT_TIMEOUT_MS` (15s, `CodemanSseStale.CONNECT_TIMEOUT_MS`) and then retried through `_handleSseConnectionFailure()`, the same backoff `onerror` uses. Its timer is cleared at the top of `connectSSE()` BEFORE the offline early return, on `open`, and in `_handleSseConnectionFailure()`; the timer and the handler both ignore an `EventSource` that has been replaced. The window `offline` handler leaves it running on purpose (`navigator.onLine` can report offline on a working network). Tests: `test/sse-connect-timeout.test.ts`. → [architecture-invariants#sse-staleness-watchdog](../../docs/architecture-invariants.md#sse-staleness-watchdog)
|
||||
|
||||
**Z-index layers** (keep new overlays consistent with this stack): local echo overlay (7; with local echo on it also draws the iOS IME composition preview, as an underlined tail after its pending text via `setComposition`), iOS IME composition preview span when local echo is off (6 inside `.xterm-helpers`, whose own z-index 5 is its EFFECTIVE layer, so it sits UNDER the overlay and must never be used while the overlay shows text), terminal touch-selection bar (900, below floating agent windows), subagent windows + split picker menu + Tiles count menu (1000), plan agents (1100), mobile/tablet fixed header (1200), modals on ≤768px (1300, must beat the fixed header), log viewers (2000), connection-loss overlay (2500), image popups (3000), response viewer (5000, backdrop 4999), folder grid (5090, above the response viewer it opens from, below the file preview its tiles open), file-preview overlay (5100, must outrank the response viewer that launches it), toasts/path picker (10000+), custom-model center-status banner (10001; its `[hidden]` must re-assert `display: none` or `dismiss()` leaves an invisible click-blocker), custom-model swap-confirm/context-warning modals (10010). → [architecture-invariants#z-index-layers](../../docs/architecture-invariants.md#z-index-layers)
|
||||
**Z-index layers** (keep new overlays consistent with this stack): local echo overlay (7; with local echo on it also draws the iOS IME composition preview, as an underlined tail after its pending text via `setComposition`), iOS IME composition preview span when local echo is off (6 inside `.xterm-helpers`, whose own z-index 5 is its EFFECTIVE layer, so it sits UNDER the overlay and must never be used while the overlay shows text), terminal touch-selection bar (900, below floating agent windows), subagent windows + split picker menu + Tiles count menu (1000), mobile/tablet fixed header (1200), modals on ≤768px (1300, must beat the fixed header), log viewers (2000), connection-loss overlay (2500), image popups (3000), response viewer (5000, backdrop 4999), folder grid (5090, above the response viewer it opens from, below the file preview its tiles open), file-preview overlay (5100, must outrank the response viewer that launches it), toasts/path picker (10000+), custom-model center-status banner (10001; its `[hidden]` must re-assert `display: none` or `dismiss()` leaves an invisible click-blocker), custom-model swap-confirm/context-warning modals (10010). → [architecture-invariants#z-index-layers](../../docs/architecture-invariants.md#z-index-layers)
|
||||
|
||||
**Keyboard shortcuts**: Escape (close), Ctrl+? (shortcut overlay), Ctrl/Cmd/Alt+K (session palette), Ctrl+Tab (next), Alt+[/] (prev/next tab), Alt+1-9 (switch tab), Ctrl+Shift+{/} (move tab left/right), Shift+Enter or Ctrl+Enter (newline), Ctrl+C (copy selection, else interrupt) / Ctrl+Shift+C (copy, never interrupts), Ctrl+L (clear), Ctrl+Shift+R (restore size), Ctrl+Shift+V (voice input), Ctrl/Cmd +/- (font), Shift+Wheel (local scrollback when mouse passthrough is active), Shift+drag (start a selection in a stripped-DECSET pane, where xterm's own Shift branch is unreachable and a Shift+drag used to select nothing; `_installShiftDragSelection`), right-click (copy the selection, the mintty/PuTTY convention, since xterm paints into a canvas and the native menu has no Copy for it; with nothing selected the native menu is left alone). Rebindable via the registry. ⚠️ **Ctrl+W is deliberately NOT bound** (owner decision): it is delete-word in every shell and agent CLI, and as Close Session it killed sessions with no confirm; `close-session` keeps `bindings: []` and stays bindable (`test/ctrl-w-never-closes.test.ts`).
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* @mixin Extends CodemanApp.prototype via Object.assign
|
||||
* @dependency app.js (CodemanApp class must be defined)
|
||||
* @loadorder 14 of 15 — loaded after ralph-wizard.js
|
||||
* @loadorder 14 of 15 — loaded after session-ui.js
|
||||
*/
|
||||
|
||||
// Codeman — Centralized API fetch helpers for CodemanApp
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* panels-ui.js — Subagent panel, agent teams, project insights, file browser, log viewer,
|
||||
* image popups, monitor, token stats, toast, system stats
|
||||
* session-ui.js — Quick start, session options modal, case settings, mobile case picker
|
||||
* ralph-wizard.js — Ralph Loop wizard modal
|
||||
* api-client.js — API helper methods (fetch wrappers)
|
||||
* subagent-windows.js — Floating subagent terminal windows
|
||||
*
|
||||
@@ -299,13 +298,6 @@ const _SSE_HANDLER_MAP = [
|
||||
[SSE_EVENTS.TUNNEL_QR_REGENERATED, '_onTunnelQrRegenerated'],
|
||||
[SSE_EVENTS.TUNNEL_QR_AUTH_USED, '_onTunnelQrAuthUsed'],
|
||||
|
||||
// Plan orchestration
|
||||
[SSE_EVENTS.PLAN_SUBAGENT, '_onPlanSubagent'],
|
||||
[SSE_EVENTS.PLAN_PROGRESS, '_onPlanProgress'],
|
||||
[SSE_EVENTS.PLAN_STARTED, '_onPlanStarted'],
|
||||
[SSE_EVENTS.PLAN_CANCELLED, '_onPlanCancelled'],
|
||||
[SSE_EVENTS.PLAN_COMPLETED, '_onPlanCompleted'],
|
||||
|
||||
// Orchestrator loop
|
||||
[SSE_EVENTS.ORCHESTRATOR_STATE_CHANGED, '_onOrchestratorStateChanged'],
|
||||
[SSE_EVENTS.ORCHESTRATOR_PLAN_PROGRESS, '_onOrchestratorPlanProgress'],
|
||||
@@ -872,17 +864,6 @@ class CodemanApp {
|
||||
this.ralphStatePanelCollapsed = true; // Default to collapsed
|
||||
this.ralphClosedSessions = new Set(); // Sessions where user explicitly closed Ralph panel
|
||||
|
||||
// Plan subagent windows (visible agents during plan generation)
|
||||
this.planSubagents = new Map(); // Map<agentId, { type, model, status, startTime, element, relativePos }>
|
||||
this.planSubagentWindowZIndex = ZINDEX_PLAN_SUBAGENT_BASE;
|
||||
this.planGenerationStopped = false; // Flag to ignore SSE events after Stop
|
||||
this.planAgentsMinimized = false; // Whether agent windows are minimized to tab
|
||||
|
||||
// Wizard dragging state
|
||||
this.wizardDragState = null; // { startX, startY, startLeft, startTop, isDragging }
|
||||
this.wizardDragListeners = null; // { move, up } for cleanup
|
||||
this.wizardPosition = null; // { left, top } - null means centered
|
||||
|
||||
// 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 }>
|
||||
@@ -5058,10 +5039,6 @@ class CodemanApp {
|
||||
// wrapping) clipped xterm's bottom rows behind the toolbar until a tab
|
||||
// switch or a window resize.
|
||||
// Clear any other orphaned timers
|
||||
if (this.planLoadingTimer) {
|
||||
clearInterval(this.planLoadingTimer);
|
||||
this.planLoadingTimer = null;
|
||||
}
|
||||
if (this.timerCountdownInterval) {
|
||||
clearInterval(this.timerCountdownInterval);
|
||||
this.timerCountdownInterval = null;
|
||||
@@ -11140,28 +11117,6 @@ class CodemanApp {
|
||||
this.renderRalphStatePanel();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Ralph Loop Wizard (methods in ralph-wizard.js)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
// Wizard state (initialized here, methods loaded from ralph-wizard.js)
|
||||
ralphWizardStep = 1;
|
||||
ralphWizardConfig = {
|
||||
taskDescription: '',
|
||||
completionPhrase: 'COMPLETE',
|
||||
maxIterations: 10,
|
||||
caseName: 'testcase',
|
||||
enableRespawn: false,
|
||||
generatedPlan: null,
|
||||
planGenerated: false,
|
||||
skipPlanGeneration: false,
|
||||
planDetailLevel: 'detailed',
|
||||
existingPlan: null,
|
||||
useExistingPlan: false,
|
||||
};
|
||||
planLoadingTimer = null;
|
||||
planLoadingStartTime = null;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Kill Sessions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* @dependency None (first in load order)
|
||||
* @loadorder 1 of 15 — constants.js → mobile-handlers.js → voice-input.js → notification-manager.js
|
||||
* → keyboard-accessory.js → input-cjk.js → app.js → terminal-ui.js → respawn-ui.js
|
||||
* → ralph-panel.js → settings-ui.js → panels-ui.js → session-ui.js → ralph-wizard.js
|
||||
* → ralph-panel.js → settings-ui.js → panels-ui.js → session-ui.js
|
||||
* → api-client.js → subagent-windows.js
|
||||
*/
|
||||
|
||||
@@ -135,7 +135,6 @@ const TUI_REDRAW_SETTLE_MS = 400; // Grace for a TUI to redraw after a
|
||||
|
||||
// Z-index base values for layered floating windows
|
||||
const ZINDEX_SUBAGENT_BASE = 1000;
|
||||
const ZINDEX_PLAN_SUBAGENT_BASE = 1100;
|
||||
const ZINDEX_LOG_VIEWER_BASE = 2000;
|
||||
const ZINDEX_IMAGE_POPUP_BASE = 3000;
|
||||
|
||||
@@ -1964,7 +1963,7 @@ function getEventCoords(e) {
|
||||
return { clientX: e.clientX, clientY: e.clientY };
|
||||
}
|
||||
|
||||
// HTML escape utility (shared by NotificationManager, CodemanApp, and ralph-wizard.js)
|
||||
// HTML escape utility (shared by NotificationManager and the CodemanApp mixins)
|
||||
const _htmlEscapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
||||
const _htmlEscapePattern = /[&<>"']/g;
|
||||
function escapeHtml(text) {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
* @dependency app.js (tab render pipeline), subagent-windows.js (window + line hooks),
|
||||
* tile-grid.js (tile mount, reveal and still-copy hooks)
|
||||
* @dependency constants.js (escapeHtml)
|
||||
* @loadorder 12.6 of 16, after webview-tabs.js, before ralph-wizard.js
|
||||
* @loadorder 12.6 of 16, after webview-tabs.js, before api-client.js
|
||||
*/
|
||||
|
||||
/** Tab entrance styles. `key` doubles as the `data-tab-anim` value. */
|
||||
|
||||
@@ -1280,16 +1280,6 @@
|
||||
<div class="monitor-empty">No mux sessions</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-section" id="monitorPlanAgentsSection" style="display: none;">
|
||||
<div class="monitor-section-header">
|
||||
<span>Plan Wizard Agents</span>
|
||||
<span class="task-stats" id="monitorPlanAgentStats">0 active</span>
|
||||
<button class="btn-toolbar btn-sm btn-danger" onclick="app.cancelPlanFromMonitor()" title="Cancel plan generation">Cancel</button>
|
||||
</div>
|
||||
<div class="monitor-section-body" id="monitorPlanAgentsBody">
|
||||
<div class="monitor-empty">No plan agents</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-section" id="monitorSubagentsSection">
|
||||
<div class="monitor-section-header">
|
||||
<span>Background Agents</span>
|
||||
@@ -4217,189 +4207,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ralph Loop Wizard Modal -->
|
||||
<div class="modal" id="ralphWizardModal">
|
||||
<div class="modal-backdrop"></div>
|
||||
<div class="modal-content modal-wizard">
|
||||
<div class="modal-header">
|
||||
<h3>Start Ralph Loop</h3>
|
||||
<div class="modal-header-actions">
|
||||
<button class="modal-close" onclick="app.closeRalphWizard()" aria-label="Close Ralph wizard">×</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wizard Progress Steps -->
|
||||
<div class="wizard-progress">
|
||||
<div class="wizard-step active" data-step="1">
|
||||
<span class="wizard-step-number">1</span>
|
||||
<span class="wizard-step-label">Describe</span>
|
||||
</div>
|
||||
<div class="wizard-step" data-step="2">
|
||||
<span class="wizard-step-number">2</span>
|
||||
<span class="wizard-step-label">Plan</span>
|
||||
</div>
|
||||
<div class="wizard-step" data-step="3">
|
||||
<span class="wizard-step-number">3</span>
|
||||
<span class="wizard-step-label">Launch</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<!-- Step 1: Task & Completion Setup -->
|
||||
<div class="wizard-page wizard-page-compact" id="ralphWizardStep1">
|
||||
<div class="wizard-step-intro">
|
||||
<p>Describe your task below. Claude will generate an implementation plan with testing steps.</p>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>What do you want to build?</label>
|
||||
<div class="textarea-with-assist">
|
||||
<textarea
|
||||
id="ralphTaskDescription"
|
||||
rows="3"
|
||||
placeholder="Build a REST API for user authentication with JWT tokens, login/logout endpoints, and password reset flow."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-inline-row">
|
||||
<div class="wizard-inline-field">
|
||||
<label>Working Directory <span id="existingPlanBadge" class="existing-plan-badge" style="display:none"></span></label>
|
||||
<div class="case-select-inline">
|
||||
<select id="ralphCaseSelect" class="form-select" onchange="app.onRalphCaseChange()"></select>
|
||||
<button class="btn-toolbar btn-sm" onclick="app.showCreateCaseModal()">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wizard-inline-field wizard-inline-field-small">
|
||||
<label>Iterations</label>
|
||||
<div class="iteration-presets-compact">
|
||||
<button type="button" class="iteration-preset-btn active" data-iterations="10" onclick="app.selectIterationPreset(10)">10</button>
|
||||
<button type="button" class="iteration-preset-btn" data-iterations="25" onclick="app.selectIterationPreset(25)">25</button>
|
||||
<button type="button" class="iteration-preset-btn" data-iterations="50" onclick="app.selectIterationPreset(50)">50</button>
|
||||
<button type="button" class="iteration-preset-btn" data-iterations="0" onclick="app.selectIterationPreset(0)">∞</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hidden completion phrase with default -->
|
||||
<input type="hidden" id="ralphCompletionPhrase" value="COMPLETE">
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Plan Generation -->
|
||||
<div class="wizard-page wizard-page-compact hidden" id="ralphWizardStep2">
|
||||
<!-- Existing plan detected (shown when @fix_plan.md exists) -->
|
||||
<div id="existingPlanSection" class="plan-section hidden">
|
||||
<!-- Content populated by app.updateExistingPlanUI() -->
|
||||
</div>
|
||||
|
||||
<!-- Loading state (shown during generation) -->
|
||||
<div id="planGenerationLoading" class="plan-loading-state hidden">
|
||||
<div class="plan-loading-content">
|
||||
<div class="plan-spinner"></div>
|
||||
<div class="plan-loading-text">
|
||||
<span class="plan-loading-title" id="planLoadingTitle">Starting Opus 4.5...</span>
|
||||
<span class="plan-loading-time" id="planLoadingTime">0s</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="plan-loading-hint" id="planLoadingHint">Initializing deep reasoning model</p>
|
||||
<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>
|
||||
<!-- Auto-start toggle visible during generation -->
|
||||
<div class="plan-autostart-toggle">
|
||||
<label class="plan-autostart-label">
|
||||
<input type="checkbox" id="ralphAutoStartStep2" onchange="app.ralphWizardConfig.autoStart = this.checked">
|
||||
<span class="plan-autostart-text">Auto-launch when plan completes</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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()">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 -->
|
||||
<div id="planEditor" class="plan-section hidden">
|
||||
<div class="plan-editor-header">
|
||||
<span>Implementation Plan <span class="plan-stats" id="planStats"></span></span>
|
||||
<div class="plan-editor-actions">
|
||||
<div class="plan-option-group">
|
||||
<label>Detail:</label>
|
||||
<div class="plan-detail-btns">
|
||||
<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: parallel subagents + verification (slower but more thorough)">Enhanced</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-toolbar btn-sm" onclick="app.regeneratePlan()">Regenerate</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="planItemsList" class="plan-items-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Preview & Launch -->
|
||||
<div class="wizard-page hidden" id="ralphWizardStep3">
|
||||
<div class="ralph-preview-section">
|
||||
<label>Prompt Preview</label>
|
||||
<div class="ralph-prompt-preview" id="ralphPromptPreview"></div>
|
||||
</div>
|
||||
|
||||
<div class="ralph-config-summary">
|
||||
<div class="config-summary-row">
|
||||
<span class="config-label">Completion Phrase:</span>
|
||||
<code id="summaryPhrase">COMPLETE</code>
|
||||
</div>
|
||||
<div class="config-summary-row">
|
||||
<span class="config-label">Max Iterations:</span>
|
||||
<span id="summaryIterations">10</span>
|
||||
</div>
|
||||
<div class="config-summary-row">
|
||||
<span class="config-label">Case:</span>
|
||||
<span id="summaryCase">testcase</span>
|
||||
</div>
|
||||
<div class="config-summary-row" style="display: none;">
|
||||
<span class="config-label">Plan:</span>
|
||||
<span id="summaryPlan">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Options (Collapsible) -->
|
||||
<details class="advanced-options">
|
||||
<summary>Advanced Options</summary>
|
||||
<div class="advanced-options-content">
|
||||
<div class="form-row form-row-switch">
|
||||
<label>Enable Respawn</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="ralphEnableRespawn">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="form-hint">Auto-restart sessions when context fills up (usually not needed)</span>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wizard Footer -->
|
||||
<div class="wizard-footer">
|
||||
<button class="btn-toolbar" id="ralphBackBtn" onclick="app.ralphWizardBack()" style="display: none;">Back</button>
|
||||
<div class="wizard-footer-right">
|
||||
<button class="btn-toolbar" onclick="app.closeRalphWizard()">Cancel</button>
|
||||
<button class="btn-toolbar btn-primary" id="ralphNextBtn" onclick="app.ralphWizardNext()">Next</button>
|
||||
<button class="btn-toolbar btn-success" id="ralphStartBtn" onclick="app.startRalphLoop()" style="display: none;">Start Loop</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Kill All Confirmation Modal -->
|
||||
<div class="modal" id="killAllModal">
|
||||
<div class="modal-backdrop" onclick="app.closeKillAllModal()"></div>
|
||||
@@ -4764,7 +4571,6 @@
|
||||
<script defer src="edit-history-ui.js"></script>
|
||||
<script defer src="local-model-ui.js"></script>
|
||||
<script defer src="entrance-animations.js"></script>
|
||||
<script defer src="ralph-wizard.js"></script>
|
||||
<script defer src="api-client.js"></script>
|
||||
<script defer src="subagent-windows.js"></script>
|
||||
<script defer src="ultracode-windows.js"></script>
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
* by Link Existing and the extended mobile keyboard bar.
|
||||
*
|
||||
* - FocusTrap (class) — Traps Tab/Shift+Tab keyboard focus within a modal element.
|
||||
* Saves and restores previously focused element on deactivate. Used by Ralph wizard
|
||||
* and other modal dialogs.
|
||||
* Saves and restores previously focused element on deactivate. Used by the modal dialogs.
|
||||
*
|
||||
* @globals {object} KeyboardAccessoryBar
|
||||
* @globals {object} PathPicker
|
||||
|
||||
@@ -1927,30 +1927,6 @@ html.mobile-init .file-browser-panel {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* Ralph wizard responsive */
|
||||
.modal-wizard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wizard-progress {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.wizard-step {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.wizard-step-number {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.wizard-step-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Timer banner - includes safe area padding */
|
||||
.timer-banner {
|
||||
padding-left: calc(0.5rem + var(--safe-area-left));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @fileoverview Ralph state panel (progress ring, status, task cards, fix_plan.md integration),
|
||||
* plan versioning (checkpoint, rollback, diff), and plan wizard agents in monitor.
|
||||
* Includes 11 SSE handlers for Ralph and plan events.
|
||||
* and plan versioning (checkpoint, rollback, diff). Includes the SSE handlers for Ralph events.
|
||||
*
|
||||
* @mixin Extends CodemanApp.prototype via Object.assign
|
||||
* @dependency app.js (CodemanApp class, this.ralphStates, this.activeSessionId)
|
||||
@@ -68,64 +67,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
this._notifySession(data.sessionId, 'warning', 'exit-gate', 'Exit Gate Met', `Loop ready to exit (indicators: ${data.completionIndicators})`);
|
||||
},
|
||||
|
||||
// Bash tools
|
||||
|
||||
// Plan orchestration
|
||||
_onPlanSubagent(data) {
|
||||
console.log('[Plan Subagent]', data);
|
||||
this.handlePlanSubagentEvent(data);
|
||||
},
|
||||
|
||||
_onPlanProgress(data) {
|
||||
console.log('[Plan Progress]', data);
|
||||
|
||||
// Update UI if we have a progress handler registered
|
||||
if (this._planProgressHandler) {
|
||||
this._planProgressHandler({ type: 'plan:progress', data });
|
||||
}
|
||||
|
||||
// Also update the loading display directly for better feedback
|
||||
const titleEl = document.getElementById('planLoadingTitle');
|
||||
const hintEl = document.getElementById('planLoadingHint');
|
||||
|
||||
if (titleEl && data.phase) {
|
||||
const phaseLabels = {
|
||||
'parallel-analysis': 'Running parallel analysis...',
|
||||
'subagent': data.detail || 'Subagent working...',
|
||||
'synthesis': 'Synthesizing results...',
|
||||
'verification': 'Running verification...',
|
||||
};
|
||||
titleEl.textContent = phaseLabels[data.phase] || data.phase;
|
||||
}
|
||||
if (hintEl && data.detail) {
|
||||
hintEl.textContent = data.detail;
|
||||
}
|
||||
},
|
||||
|
||||
_onPlanStarted(data) {
|
||||
console.log('[Plan Started]', data);
|
||||
this.activePlanOrchestratorId = data.orchestratorId;
|
||||
this.planGenerationStopped = false; // Reset flag for new generation
|
||||
this.renderMonitorPlanAgents();
|
||||
},
|
||||
|
||||
_onPlanCancelled(data) {
|
||||
console.log('[Plan Cancelled]', data);
|
||||
if (this.activePlanOrchestratorId === data.orchestratorId) {
|
||||
this.activePlanOrchestratorId = null;
|
||||
}
|
||||
this.renderMonitorPlanAgents();
|
||||
},
|
||||
|
||||
_onPlanCompleted(data) {
|
||||
console.log('[Plan Completed]', data);
|
||||
if (this.activePlanOrchestratorId === data.orchestratorId) {
|
||||
this.activePlanOrchestratorId = null;
|
||||
}
|
||||
this.renderMonitorPlanAgents();
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Enhanced Ralph Wiggum Loop Panel
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
@@ -1095,91 +1036,4 @@ Object.assign(CodemanApp.prototype, {
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
return `${days}d ago`;
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Plan Wizard Agents in Monitor
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
renderMonitorPlanAgents() {
|
||||
const section = document.getElementById('monitorPlanAgentsSection');
|
||||
const body = document.getElementById('monitorPlanAgentsBody');
|
||||
const stats = document.getElementById('monitorPlanAgentStats');
|
||||
if (!section || !body) return;
|
||||
|
||||
const planAgents = Array.from(this.planSubagents?.values() || []);
|
||||
const hasActiveOrchestrator = !!this.activePlanOrchestratorId;
|
||||
|
||||
// Show section only if there are plan agents or active orchestrator
|
||||
if (planAgents.length === 0 && !hasActiveOrchestrator) {
|
||||
section.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
section.style.display = '';
|
||||
|
||||
const activeCount = planAgents.filter(a => a.status === 'running').length;
|
||||
const completedCount = planAgents.filter(a => a.status === 'completed' || a.status === 'failed').length;
|
||||
|
||||
if (stats) {
|
||||
if (hasActiveOrchestrator) {
|
||||
stats.textContent = `${activeCount} running, ${completedCount} done`;
|
||||
} else {
|
||||
stats.textContent = `${planAgents.length} total`;
|
||||
}
|
||||
}
|
||||
|
||||
if (planAgents.length === 0) {
|
||||
body.innerHTML = `<div class="monitor-empty">${hasActiveOrchestrator ? 'Plan generation starting...' : 'No plan agents'}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
for (const agent of planAgents) {
|
||||
const statusClass = agent.status === 'running' ? 'active' : agent.status === 'completed' ? 'completed' : 'error';
|
||||
const agentLabel = agent.agentType || agent.agentId;
|
||||
const modelBadge = agent.model ? `<span class="model-badge opus">opus</span>` : '';
|
||||
const detail = agent.detail ? escapeHtml(agent.detail.substring(0, 50)) : '';
|
||||
const duration = agent.durationMs ? `${(agent.durationMs / 1000).toFixed(1)}s` : '';
|
||||
const itemCount = agent.itemCount ? `${agent.itemCount} items` : '';
|
||||
|
||||
html += `
|
||||
<div class="process-item">
|
||||
<span class="process-mode ${statusClass}">${agent.status || 'pending'}</span>
|
||||
<div class="process-info">
|
||||
<div class="process-name">${modelBadge} ${escapeHtml(agentLabel)}</div>
|
||||
<div class="process-meta">
|
||||
${detail ? `<span>${detail}</span>` : ''}
|
||||
${itemCount ? `<span>${itemCount}</span>` : ''}
|
||||
${duration ? `<span>${duration}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
body.innerHTML = html;
|
||||
},
|
||||
|
||||
async cancelPlanFromMonitor() {
|
||||
if (!this.activePlanOrchestratorId && this.planSubagents?.size === 0) {
|
||||
this.showToast('No active plan generation', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Cancel plan generation and close all plan agent windows?')) return;
|
||||
|
||||
// Cancel the plan generation (reuse existing method)
|
||||
await this.cancelPlanGeneration();
|
||||
|
||||
// Also force close the wizard if it's open
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
if (wizardModal?.classList.contains('active')) {
|
||||
this.closeRalphWizard();
|
||||
}
|
||||
|
||||
// Update monitor display
|
||||
this.renderMonitorPlanAgents();
|
||||
this.showToast('Plan generation cancelled', 'success');
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,7 +8,7 @@
|
||||
* @dependency app.js (CodemanApp class, this.sessions, this.cases, this.activeSessionId)
|
||||
* @dependency constants.js (escapeHtml)
|
||||
* @dependency mobile-handlers.js (MobileDetection)
|
||||
* @loadorder 12 of 15 — loaded after panels-ui.js, before ralph-wizard.js
|
||||
* @loadorder 12 of 15 — loaded after panels-ui.js, before api-client.js
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -11335,47 +11335,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
drop-shadow(0 0 12px rgba(59, 130, 246, 0.7));
|
||||
}
|
||||
|
||||
/* Wizard connection lines - different color to show plan generation link */
|
||||
.connection-line.wizard-connection {
|
||||
stroke: #60a5fa;
|
||||
stroke-width: 3;
|
||||
/* Dark outline for contrast against blurred backgrounds */
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.8))
|
||||
drop-shadow(0 0 5px #60a5fa)
|
||||
drop-shadow(0 0 10px #60a5fa);
|
||||
stroke-dasharray: 8 4;
|
||||
animation: wizard-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes wizard-pulse {
|
||||
0%, 100% { opacity: 0.75; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* Plan subagent to regular subagent connection lines (Opus → Haiku) */
|
||||
.connection-line.plan-to-subagent-line {
|
||||
stroke: #3b82f6;
|
||||
stroke-width: 3;
|
||||
/* Dark outline for contrast, vibrant blue glow */
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.8))
|
||||
drop-shadow(0 0 4px rgba(59, 130, 246, 0.8))
|
||||
drop-shadow(0 0 8px rgba(59, 130, 246, 0.5));
|
||||
stroke-dasharray: 5 3;
|
||||
animation: plan-subagent-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.connection-line.plan-to-subagent-line:hover {
|
||||
stroke-width: 3.5;
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9))
|
||||
drop-shadow(0 0 6px rgba(59, 130, 246, 1))
|
||||
drop-shadow(0 0 12px rgba(59, 130, 246, 0.7));
|
||||
}
|
||||
|
||||
@keyframes plan-subagent-pulse {
|
||||
0%, 100% { opacity: 0.8; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* ===== Session lineage lines (tab → tab it spawned, session-lineage.js) =====
|
||||
One quiet orthogonal tree per spawning tab, every family always drawn and
|
||||
routed through the gaps between tab rows (computeLineageTree in constants.js).
|
||||
@@ -12623,565 +12582,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ========== Ralph Loop Wizard ========== */
|
||||
|
||||
/* Wizard Progress Indicator */
|
||||
.wizard-progress {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--bg-dark);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.wizard-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.wizard-step.active {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.wizard-step.completed {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.wizard-step-number {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 50%;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wizard-step.active .wizard-step-number {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wizard-step.completed .wizard-step-number {
|
||||
background: var(--green);
|
||||
border-color: var(--green);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wizard-step-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Wizard Pages */
|
||||
.wizard-page {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.wizard-page.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Wizard Footer */
|
||||
.wizard-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.wizard-footer-right {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Wizard Modal - compact layout */
|
||||
.modal-wizard {
|
||||
width: 480px;
|
||||
max-width: 95vw;
|
||||
max-height: 70vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Draggable wizard state */
|
||||
.modal-wizard.draggable {
|
||||
position: absolute;
|
||||
transform: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.modal-wizard .modal-body {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.modal-wizard .wizard-footer {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wizard-page-compact {
|
||||
min-height: auto;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.wizard-step-intro {
|
||||
text-align: center;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.wizard-step-intro p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Wizard inline row for compact layout */
|
||||
.wizard-inline-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.wizard-inline-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.wizard-inline-field-small {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.wizard-inline-field label {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.iteration-presets-compact {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.iteration-presets-compact .iteration-preset-btn {
|
||||
padding: 0.35rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
min-width: 36px;
|
||||
}
|
||||
|
||||
/* Plan Generation Step */
|
||||
.plan-section {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.plan-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Existing Plan Badge (in case selector) */
|
||||
.existing-plan-badge {
|
||||
font-size: 0.7rem;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
margin-left: 0.5rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Existing Plan Card (Step 2) */
|
||||
.existing-plan-card {
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.existing-plan-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.existing-plan-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.existing-plan-stats {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.existing-plan-stats .stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.existing-plan-stats .stat.pending {
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.existing-plan-stats .stat.completed {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.existing-plan-stats .stat.total {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.existing-plan-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Plan Loading State */
|
||||
.plan-loading-state {
|
||||
padding: 1.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.plan-loading-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.plan-spinner {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 3px solid var(--border);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.plan-loading-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.plan-loading-title {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.plan-loading-time {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
|
||||
.plan-loading-hint {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.85rem;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.plan-error-msg {
|
||||
color: var(--red);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.plan-option-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-option-group label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.plan-option-group select {
|
||||
padding: 0.3rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.plan-detail-btns {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.plan-detail-btn {
|
||||
padding: 0.3rem 0.6rem;
|
||||
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;
|
||||
}
|
||||
|
||||
.plan-detail-btn:hover {
|
||||
border-color: var(--border-light);
|
||||
}
|
||||
|
||||
.plan-detail-btn.active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Plan Editor */
|
||||
.plan-editor-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.plan-stats {
|
||||
font-weight: normal;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-editor-actions {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.plan-items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-loading-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plan-autostart-toggle {
|
||||
margin-top: 1.25rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.plan-autostart-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plan-autostart-label input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.plan-autostart-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.plan-autostart-label:hover .plan-autostart-text {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Existing wizard files section */
|
||||
|
||||
.plan-cancel-btn {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Plan Stopped State */
|
||||
.plan-stopped-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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-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);
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.plan-subagent-header:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Clickable status text */
|
||||
.plan-subagent-status-text.clickable {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.plan-subagent-status-text.clickable:hover {
|
||||
text-decoration-style: solid;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Ralph panel version info */
|
||||
.ralph-version-info {
|
||||
display: flex;
|
||||
@@ -13212,129 +12612,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Textarea with AI Assist button */
|
||||
.textarea-with-assist {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.textarea-with-assist textarea {
|
||||
width: 100%;
|
||||
padding: 0.6rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.85rem;
|
||||
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.textarea-with-assist textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Iteration Presets */
|
||||
|
||||
.iteration-preset-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text-dim);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.iteration-preset-btn:hover {
|
||||
border-color: var(--border-light);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.iteration-preset-btn.active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Case Select Inline */
|
||||
.case-select-inline {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.case-select-inline select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Ralph Prompt Preview */
|
||||
.ralph-preview-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ralph-preview-section > label {
|
||||
display: block;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 0.35rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.ralph-prompt-preview {
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.ralph-prompt-preview .preview-highlight {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Config Summary */
|
||||
.ralph-config-summary {
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.config-summary-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.35rem 0;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.config-summary-row:not(:last-child) {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.config-label {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.config-summary-row code {
|
||||
background: var(--bg-dark);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* Advanced Options */
|
||||
.advanced-options {
|
||||
margin-top: 1rem;
|
||||
|
||||
@@ -243,11 +243,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
const svg = document.getElementById('connectionLines');
|
||||
if (!svg) return;
|
||||
|
||||
// Check if Ralph wizard modal is open
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
const wizardOpen = wizardModal?.classList.contains('active');
|
||||
const wizardContent = wizardOpen ? wizardModal.querySelector('.modal-content') : null;
|
||||
|
||||
// Collect visible regular subagent windows
|
||||
const visibleSubagentWindows = [];
|
||||
for (const [agentId, windowInfo] of this.subagentWindows) {
|
||||
@@ -257,11 +252,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
visibleSubagentWindows.push({ agentId, windowInfo, win });
|
||||
}
|
||||
|
||||
// Get plan subagent windows as array for distribution
|
||||
const planSubagentArray = Array.from(this.planSubagents.entries())
|
||||
.filter(([, data]) => data.element)
|
||||
.map(([id, data]) => ({ id, ...data }));
|
||||
|
||||
// === PHASE 1: Batch all layout reads (getBoundingClientRect) ===
|
||||
// Reading layout properties forces the browser to calculate layout.
|
||||
// By batching all reads before any writes, we avoid repeated forced reflows.
|
||||
@@ -272,36 +262,14 @@ Object.assign(CodemanApp.prototype, {
|
||||
rects.set('sub:' + agentId, win.getBoundingClientRect());
|
||||
}
|
||||
|
||||
// Read all plan subagent rects
|
||||
for (const planAgent of planSubagentArray) {
|
||||
rects.set('plan:' + planAgent.id, planAgent.element.getBoundingClientRect());
|
||||
}
|
||||
|
||||
// Read wizard rect (if open)
|
||||
let wizardRect = null;
|
||||
if (wizardOpen && wizardContent) {
|
||||
wizardRect = wizardContent.getBoundingClientRect();
|
||||
}
|
||||
|
||||
// Read tab rects for normal mode (only tabs that are actually needed).
|
||||
// Read tab rects (only tabs that are actually needed).
|
||||
// Only a painted row is cached: a row a search or filter hid has no line
|
||||
// (_paintedSessionTab), rather than one drawn from the viewport's corner.
|
||||
if (!wizardOpen) {
|
||||
for (const { agentId } of visibleSubagentWindows) {
|
||||
const parentSessionId = this.subagentParentMap.get(agentId);
|
||||
if (!parentSessionId || rects.has('tab:' + parentSessionId)) continue;
|
||||
const tab = this._paintedSessionTab(parentSessionId);
|
||||
if (tab) rects.set('tab:' + parentSessionId, tab.getBoundingClientRect());
|
||||
}
|
||||
}
|
||||
|
||||
// Read plan window rects for wizard-to-plan lines
|
||||
if (wizardOpen && wizardContent && this.planSubagents.size > 0 && !this.planAgentsMinimized) {
|
||||
for (const [agentId, windowData] of this.planSubagents) {
|
||||
if (!windowData.element) continue;
|
||||
const key = 'planwin:' + agentId;
|
||||
if (!rects.has(key)) rects.set(key, windowData.element.getBoundingClientRect());
|
||||
}
|
||||
for (const { agentId } of visibleSubagentWindows) {
|
||||
const parentSessionId = this.subagentParentMap.get(agentId);
|
||||
if (!parentSessionId || rects.has('tab:' + parentSessionId)) continue;
|
||||
const tab = this._paintedSessionTab(parentSessionId);
|
||||
if (tab) rects.set('tab:' + parentSessionId, tab.getBoundingClientRect());
|
||||
}
|
||||
|
||||
// === PHASE 2: DOM writes using cached rects (no more layout reads) ===
|
||||
@@ -310,149 +278,34 @@ Object.assign(CodemanApp.prototype, {
|
||||
for (const { agentId } of visibleSubagentWindows) {
|
||||
const winRect = rects.get('sub:' + agentId);
|
||||
|
||||
// If wizard is open with plan subagents, connect regular subagents to plan subagent windows
|
||||
if (wizardOpen && wizardContent && planSubagentArray.length > 0) {
|
||||
// Find the nearest plan subagent window to connect to
|
||||
let nearestPlanAgent = null;
|
||||
let nearestDistance = Infinity;
|
||||
// Connect the agent window to its parent TAB.
|
||||
// Use the PERSISTENT subagentParentMap as the ONLY source of truth
|
||||
const parentSessionId = this.subagentParentMap.get(agentId);
|
||||
|
||||
for (const planAgent of planSubagentArray) {
|
||||
const planRect = rects.get('plan:' + planAgent.id);
|
||||
const planCenterX = planRect.left + planRect.width / 2;
|
||||
const planCenterY = planRect.top + planRect.height / 2;
|
||||
const winCenterX = winRect.left + winRect.width / 2;
|
||||
const winCenterY = winRect.top + winRect.height / 2;
|
||||
const distance = Math.hypot(planCenterX - winCenterX, planCenterY - winCenterY);
|
||||
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestPlanAgent = planAgent;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestPlanAgent) {
|
||||
const planRect = rects.get('plan:' + nearestPlanAgent.id);
|
||||
|
||||
// Draw line from plan subagent window to regular subagent window
|
||||
let x1, y1, x2, y2;
|
||||
const planCenterX = planRect.left + planRect.width / 2;
|
||||
const winCenterX = winRect.left + winRect.width / 2;
|
||||
|
||||
if (winCenterX < planCenterX) {
|
||||
x1 = planRect.left;
|
||||
y1 = planRect.top + planRect.height / 2;
|
||||
x2 = winRect.right;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
} else {
|
||||
x1 = planRect.right;
|
||||
y1 = planRect.top + planRect.height / 2;
|
||||
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 plan-to-subagent-line');
|
||||
line.setAttribute('data-agent-id', agentId);
|
||||
line.setAttribute('data-plan-agent-id', nearestPlanAgent.id);
|
||||
svg.appendChild(line);
|
||||
}
|
||||
} else if (wizardOpen && wizardContent) {
|
||||
// Wizard open but no plan subagents - connect directly to wizard
|
||||
const winCenterX = winRect.left + winRect.width / 2;
|
||||
const wizardCenterX = wizardRect.left + wizardRect.width / 2;
|
||||
|
||||
let x1, y1, x2, y2;
|
||||
|
||||
if (winCenterX < wizardCenterX) {
|
||||
x1 = wizardRect.left;
|
||||
y1 = wizardRect.top + wizardRect.height / 2;
|
||||
x2 = winRect.right;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
} else {
|
||||
x1 = wizardRect.right;
|
||||
y1 = wizardRect.top + wizardRect.height / 2;
|
||||
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');
|
||||
line.setAttribute('data-agent-id', agentId);
|
||||
svg.appendChild(line);
|
||||
} else {
|
||||
// NORMAL MODE: Connect agent window to its parent TAB
|
||||
// Use the PERSISTENT subagentParentMap as the ONLY source of truth
|
||||
const parentSessionId = this.subagentParentMap.get(agentId);
|
||||
|
||||
if (!parentSessionId) {
|
||||
// No parent known yet - skip this agent
|
||||
continue;
|
||||
}
|
||||
|
||||
const tabRect = rects.get('tab:' + parentSessionId);
|
||||
if (!tabRect) {
|
||||
// Tab not painted (session closed, collapsed group, or hidden by a search)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw a curved line from the tab to the window. Header strip: tab
|
||||
// bottom-center → window top-center (vertical). Sidebar: tab right-edge →
|
||||
// window left-edge (horizontal), otherwise the curve loops backwards
|
||||
// underneath the sidebar. _tabAnchor/_tabConnectorPath live in app.js.
|
||||
const anchor = this._tabAnchor(tabRect);
|
||||
const path = this._tabConnectorPath(anchor, winRect);
|
||||
|
||||
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);
|
||||
line.setAttribute('data-parent-tab', parentSessionId);
|
||||
svg.appendChild(line);
|
||||
if (!parentSessionId) {
|
||||
// No parent known yet - skip this agent
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw lines from wizard to plan subagent windows (Opus agents during plan generation)
|
||||
// Skip if agents are minimized to tab
|
||||
if (wizardOpen && wizardContent && this.planSubagents.size > 0 && !this.planAgentsMinimized) {
|
||||
for (const [agentId] of this.planSubagents) {
|
||||
const winRect = rects.get('planwin:' + agentId);
|
||||
if (!winRect) continue;
|
||||
|
||||
// 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) {
|
||||
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 {
|
||||
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);
|
||||
const tabRect = rects.get('tab:' + parentSessionId);
|
||||
if (!tabRect) {
|
||||
// Tab not painted (session closed, collapsed group, or hidden by a search)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw a curved line from the tab to the window. Header strip: tab
|
||||
// bottom-center → window top-center (vertical). Sidebar: tab right-edge →
|
||||
// window left-edge (horizontal), otherwise the curve loops backwards
|
||||
// underneath the sidebar. _tabAnchor/_tabConnectorPath live in app.js.
|
||||
const anchor = this._tabAnchor(tabRect);
|
||||
const path = this._tabConnectorPath(anchor, winRect);
|
||||
|
||||
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);
|
||||
line.setAttribute('data-parent-tab', parentSessionId);
|
||||
svg.appendChild(line);
|
||||
}
|
||||
|
||||
// Ultracode floating run windows → parent tab (additional layer, ultracode-windows.js).
|
||||
@@ -627,43 +480,9 @@ Object.assign(CodemanApp.prototype, {
|
||||
finalY = topStart + visibleCount * (mobileCardHeight + mobileCardGap);
|
||||
}
|
||||
} else {
|
||||
// Check if Ralph wizard modal is open - if so, position windows on the sides
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
const wizardOpen = wizardModal?.classList.contains('active');
|
||||
|
||||
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 = WINDOW_INITIAL_TOP_PX;
|
||||
maxCols = Math.floor((viewportWidth - startX - 50) / (windowWidth + gap)) || 1;
|
||||
}
|
||||
const startX = 50;
|
||||
const startY = WINDOW_INITIAL_TOP_PX;
|
||||
const maxCols = Math.floor((viewportWidth - startX - 50) / (windowWidth + gap)) || 1;
|
||||
|
||||
const maxRows = Math.floor((viewportHeight - startY - 50) / (windowHeight + gap)) || 1;
|
||||
const col = windowCount % maxCols;
|
||||
@@ -986,20 +805,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
this.logViewerWindows.clear();
|
||||
|
||||
// Clean up plan subagent windows (wizard agents)
|
||||
if (this.planSubagents) {
|
||||
for (const windowData of this.planSubagents.values()) {
|
||||
if (windowData.dragListeners) {
|
||||
document.removeEventListener('mousemove', windowData.dragListeners.move);
|
||||
document.removeEventListener('mouseup', windowData.dragListeners.up);
|
||||
}
|
||||
if (windowData.element) {
|
||||
windowData.element.remove();
|
||||
}
|
||||
}
|
||||
this.planSubagents.clear();
|
||||
}
|
||||
|
||||
// Clean up all image popup windows with their drag listeners
|
||||
for (const popupData of this.imagePopups.values()) {
|
||||
if (popupData.dragListeners) {
|
||||
@@ -1018,25 +823,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Clean up ultracode floating run windows (re-seeded from data.workflowRuns on reconnect)
|
||||
if (typeof this.removeAllUltracodeWindows === 'function') this.removeAllUltracodeWindows();
|
||||
|
||||
// Clear orphaned plan generation state
|
||||
this.activePlanOrchestratorId = null;
|
||||
this._planProgressHandler = null;
|
||||
this.planGenerationStopped = true;
|
||||
if (this.planGenerationAbortController) {
|
||||
this.planGenerationAbortController.abort();
|
||||
this.planGenerationAbortController = null;
|
||||
}
|
||||
|
||||
// Clean up wizard-specific timers (leak fix: not cleared on SSE reconnect)
|
||||
if (this.wizardMinimizedTimer) {
|
||||
clearInterval(this.wizardMinimizedTimer);
|
||||
this.wizardMinimizedTimer = null;
|
||||
}
|
||||
|
||||
// Clean up wizard drag listeners (leak fix: document-level handlers)
|
||||
if (typeof this.cleanupWizardDragging === 'function') this.cleanupWizardDragging();
|
||||
|
||||
// Deactivate focus trap if wizard was open (leak fix: keydown listener)
|
||||
// Deactivate a focus trap a modal left active (leak fix: keydown listener)
|
||||
if (this.activeFocusTrap) {
|
||||
this.activeFocusTrap.deactivate();
|
||||
this.activeFocusTrap = null;
|
||||
@@ -1045,9 +832,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Clear minimized agents tracking
|
||||
this.minimizedSubagents.clear();
|
||||
|
||||
// Update monitor panel
|
||||
this.renderMonitorPlanAgents();
|
||||
|
||||
// Update connection lines (should be empty now)
|
||||
this.updateConnectionLines();
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user