mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
chore(web): remove frontend code and styles nothing reaches
The frontend JS is outside ESLint and knip, so this pass used an AST scan (every method, function and top-level constant in src/web/public, counted against identifiers, strings and index.html) plus a selector scan of both stylesheets. Each hit was confirmed by text search before it went. JavaScript - The ten run<Cli>() one-line wrappers and the quickStart() alias: run() has dispatched to _runCliMode(mode) since the launcher consolidation and nothing called the wrappers except tests, which now call the launcher. - The Agent Teams panel code that lost its event listeners in February: the team tasks panel, the teammate pane window, rebuildTeammateMap and the three maps that nothing filled any more. - Unreferenced methods: redockSession, refreshOrchestratorStatus, toggleSubagentPanel, minimizeSubagentWindow, openAllActiveSubagentWindows, pickBetterPath, getTodoIcon, deletePreset, closeRunSummary, saveSessionOptions, getAgentParentSessionId, _isUltracodeAgentMinimized. - Six unused constants and a few unused locals (an unused frame-time computation in the touch scroll loop among them). CSS - 62 classes and one id that no markup, script or test names: the old settings-item / settings-section layout, the pre-redesign respawn and context-settings rows, the existing-wizard card and others. 116 rules and about 770 lines. A dead class inside an :is() list only loses that alternative; the rule stays. Checked in a browser against an isolated instance: no console errors on desktop or phone, a shell session runs, and 96 screenshots (desktop, tablet and phone; settings, session options, add case, panels) are pixel-identical before and after, apart from clock times. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
f604deba3e
commit
6fb8b27768
17 files changed
+48
-1298
No files matched your search
@@ -863,11 +863,6 @@ class CodemanApp {
|
||||
// Once set, never recalculated. Persisted to localStorage and server.
|
||||
this.subagentParentMap = new Map();
|
||||
|
||||
// Agent Teams tracking
|
||||
this.teams = new Map(); // Map<teamName, TeamConfig>
|
||||
this.teamTasks = new Map(); // Map<teamName, TeamTask[]>
|
||||
this.teammateMap = new Map(); // Map<agentId-prefix, {name, color, teamName}> for quick lookup
|
||||
|
||||
// Teammate tmux pane terminals (Agent Teams feature)
|
||||
this.teammatePanesByName = new Map(); // Map<name, { paneTarget, sessionId, color }>
|
||||
this.teammateTerminals = new Map(); // Map<agentId, { terminal, fitAddon, paneTarget, sessionId, resizeObserver }>
|
||||
@@ -1819,15 +1814,6 @@ class CodemanApp {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Re-dock a session: close its window (which re-docks via its unload
|
||||
* announcement) and clear dashboard state now. */
|
||||
redockSession(id) {
|
||||
const win = this.detachedWindows.get(id);
|
||||
if (win && !win.closed) { try { win.close(); } catch {} }
|
||||
this._postWindowMessage({ type: 'close-request', id });
|
||||
this._redock(id);
|
||||
}
|
||||
|
||||
/** Clear all dashboard-side detached state/timers for a session, and take its
|
||||
* sizing back: the popup owned the pane while it was open, so the dashboard's
|
||||
* record of it is stale and the session it is showing needs re-measuring.
|
||||
@@ -4989,8 +4975,6 @@ class CodemanApp {
|
||||
this._xtermSnapshots?.clear();
|
||||
}
|
||||
this.projectInsights.clear();
|
||||
this.teams.clear();
|
||||
this.teamTasks.clear();
|
||||
// Clear all idle timers to prevent stale timers from firing
|
||||
for (const timer of this.idleTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
|
||||
@@ -112,8 +112,7 @@ const MOBILE_RESIZE_RETRY_MS = 30000; // Small-viewport resize re-send whi
|
||||
const AUTO_CLOSE_NOTIFICATION_MS = 8000; // Auto-close browser notifications
|
||||
const DEFAULT_TOAST_DURATION_MS = 3000; // How long a corner toast stays by default
|
||||
const MIN_NOTIFICATION_DURATION_MS = 1000; // Shortest configurable toast / browser-notification time
|
||||
const MAX_NOTIFICATION_DURATION_MS = 300000; // Longest configurable toast / browser-notification time
|
||||
const THROTTLE_DELAY_MS = 100; // General UI throttle delay
|
||||
const MAX_NOTIFICATION_DURATION_MS = 300000; // General UI throttle delay
|
||||
const TERMINAL_CHUNK_SIZE = 32 * 1024; // 32KB chunks for terminal buffer loading
|
||||
const TERMINAL_TAIL_SIZE = 1024 * 1024; // 1MB tail for initial load (more scrollback on tab switch)
|
||||
// A shell's automatic loads (tab select, drop recovery, a tile's load) read
|
||||
@@ -131,8 +130,7 @@ const TERMINAL_TAIL_SIZE = 1024 * 1024; // 1MB tail for initial load (more s
|
||||
const SHELL_LOAD_HISTORY_LINES = 10000;
|
||||
const SHELL_LOAD_BASE_QUERY = `full=1&tail=${TERMINAL_TAIL_SIZE}&alt=recording`;
|
||||
const SHELL_LOAD_QUERY = `${SHELL_LOAD_BASE_QUERY}&lines=${SHELL_LOAD_HISTORY_LINES}`;
|
||||
const SYNC_WAIT_TIMEOUT_MS = 50; // Wait timeout for terminal sync
|
||||
const STATS_POLLING_INTERVAL_MS = 2000; // System stats polling
|
||||
const SYNC_WAIT_TIMEOUT_MS = 50; // System stats polling
|
||||
const TUI_REDRAW_SETTLE_MS = 400; // Grace for a TUI to redraw after a real resize, before fetching its buffer
|
||||
|
||||
// Z-index base values for layered floating windows
|
||||
@@ -143,10 +141,6 @@ const ZINDEX_IMAGE_POPUP_BASE = 3000;
|
||||
|
||||
// Subagent/floating window layout
|
||||
const WINDOW_INITIAL_TOP_PX = 120;
|
||||
const WINDOW_CASCADE_OFFSET_PX = 30;
|
||||
const WINDOW_MIN_WIDTH_PX = 200;
|
||||
const WINDOW_MIN_HEIGHT_PX = 200;
|
||||
const WINDOW_DEFAULT_WIDTH_PX = 300;
|
||||
|
||||
// WebGL renderer auto-fallback thresholds.
|
||||
// _installWebGLLongTaskGuard() observes longtask entries and disables WebGL
|
||||
|
||||
@@ -226,15 +226,6 @@ html.mobile-init .file-browser-panel {
|
||||
min-height: 56px;
|
||||
}
|
||||
|
||||
/* Settings grid stays 2-col on tablet but tighter */
|
||||
.settings-grid {
|
||||
gap: 0.4rem 0.75rem;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Event type grid - slightly tighter */
|
||||
.event-type-grid {
|
||||
grid-template-columns: 1fr 40px 40px 40px;
|
||||
@@ -1774,11 +1765,6 @@ html.mobile-init .file-browser-panel {
|
||||
min-height: 0; /* Allow flex item to shrink below content size */
|
||||
}
|
||||
|
||||
/* Show CLI info bar on mobile (Claude version/model) */
|
||||
.cli-info-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Mobile terminal — JS touch handler (terminal.scrollLines()) drives
|
||||
scrollback because xterm.js DOM renderer doesn't populate xterm-viewport's
|
||||
scroll area. touch-action:none lets our JS handler own the gesture. */
|
||||
@@ -2095,27 +2081,6 @@ html.mobile-init .file-browser-panel {
|
||||
|
||||
/* ---- Session Options Modal: Respawn Tab Mobile Optimizations ---- */
|
||||
|
||||
/* Respawn header — keep horizontal, compact */
|
||||
.respawn-header {
|
||||
flex-direction: row;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Enable/Stop buttons — compact */
|
||||
.respawn-header .respawn-actions {
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.respawn-header .respawn-actions .btn-toolbar {
|
||||
min-height: 28px !important;
|
||||
font-size: 0.7rem !important;
|
||||
padding: 0.2rem 0.5rem !important;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Duration preset buttons — one compact row */
|
||||
.duration-presets {
|
||||
display: flex;
|
||||
@@ -2148,28 +2113,6 @@ html.mobile-init .file-browser-panel {
|
||||
padding: 0.1rem 0.3rem;
|
||||
}
|
||||
|
||||
/* Preset selector row — full-width dropdown, buttons below */
|
||||
.preset-selector {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.preset-selector select {
|
||||
grid-column: 1 / -1;
|
||||
min-height: 32px;
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
border-radius: 5px;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
|
||||
.preset-selector .btn {
|
||||
min-height: 32px;
|
||||
font-size: 0.65rem;
|
||||
border-radius: 5px;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
|
||||
/* Textareas — prevent iOS zoom with 16px font, compact height */
|
||||
#sessionOptionsModal textarea {
|
||||
font-size: 16px;
|
||||
@@ -2204,21 +2147,6 @@ html.mobile-init .file-browser-panel {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
/* Respawn-loop box title matches the auto-resume title; its cycle-step
|
||||
checkboxes match the step labels */
|
||||
#sessionOptionsModal .respawn-loop-title {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .respawn-loop-box .checkbox-inline {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
/* Respawn options row — stack if needed */
|
||||
#sessionOptionsModal .respawn-options-row {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Form section headers — compact on mobile */
|
||||
#sessionOptionsModal .form-section-header {
|
||||
margin-top: 0.75rem;
|
||||
@@ -2244,30 +2172,6 @@ html.mobile-init .file-browser-panel {
|
||||
|
||||
/* ---- Context Tab: Mobile Optimizations ---- */
|
||||
|
||||
/* Context settings cards — tighter padding */
|
||||
.context-setting {
|
||||
padding: 0.625rem;
|
||||
}
|
||||
|
||||
/* Stack context setting header vertically on narrow screens */
|
||||
.context-setting-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.context-setting-header .input-suffix-sm input {
|
||||
width: 60px;
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.context-setting input[type="text"] {
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
min-height: 36px;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Color picker — larger swatches for touch */
|
||||
.color-picker {
|
||||
gap: 8px;
|
||||
@@ -2285,27 +2189,8 @@ html.mobile-init .file-browser-panel {
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
/* Number inputs in context tab — prevent iOS zoom */
|
||||
.context-settings-grid input[type="number"] {
|
||||
font-size: 16px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
/* ---- Ralph Tab: Mobile Optimizations ---- */
|
||||
|
||||
/* Ralph limits grid — 2 columns on mobile */
|
||||
.ralph-limits-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.ralph-limits-grid .form-col input[type="number"] {
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
min-height: 36px;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Ralph phrase input — prevent iOS zoom */
|
||||
#modalRalphPhrase {
|
||||
font-size: 16px;
|
||||
@@ -2313,18 +2198,6 @@ html.mobile-init .file-browser-panel {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Save button — full width, proper touch target */
|
||||
.ralph-config-actions {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.ralph-config-actions .btn-toolbar {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
font-size: 0.85rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ---- Summary Tab: Mobile Optimizations ---- */
|
||||
|
||||
/* Filter buttons — larger touch targets */
|
||||
@@ -2587,31 +2460,6 @@ html.mobile-init .file-browser-panel {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Settings grid - single column on phones */
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.settings-grid-3col {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
padding: 0.35rem 0.5rem;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.settings-item-label {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.settings-section-header {
|
||||
font-size: 0.6rem;
|
||||
padding: 0.35rem 0 0.2rem 0;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Form rows - compact on mobile */
|
||||
.form-row {
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -3560,12 +3408,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Show dropdown on tap instead of hover */
|
||||
.subagent-dropdown-trigger:active + .subagent-dropdown-menu,
|
||||
.subagent-dropdown-trigger:focus + .subagent-dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Press feedback for the touch bars. The pressed look is transform + filter,
|
||||
never a background or a shadow: the skin block (html:not([data-skin="og"]),
|
||||
specificity 0,2,1) and the per-run-mode colors own the background and its
|
||||
|
||||
@@ -255,19 +255,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
async refreshOrchestratorStatus() {
|
||||
try {
|
||||
const res = await fetch('/api/orchestrator/status');
|
||||
const data = await res.json();
|
||||
if (data.data?.ok) {
|
||||
this.orchestratorState = data.data;
|
||||
this.renderOrchestratorPanel();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Orchestrator] Status fetch failed:', err);
|
||||
}
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Rendering
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
+2
-320
@@ -1668,11 +1668,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Subagent Panel (Claude Code Background Agents)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
// Legacy alias
|
||||
toggleSubagentPanel() {
|
||||
this.toggleSubagentsPanel();
|
||||
},
|
||||
|
||||
updateSubagentBadge() {
|
||||
const badge = this.$('subagentCountBadge');
|
||||
const activeCount = Array.from(this.subagents.values()).filter(s => s.status === 'active' || s.status === 'idle').length;
|
||||
@@ -2253,7 +2248,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// If cleanupData is true, also remove activity and toolResults data to prevent memory leaks
|
||||
closeSessionSubagentWindows(sessionId, cleanupData = false) {
|
||||
const toClose = [];
|
||||
for (const [agentId, _windowData] of this.subagentWindows) {
|
||||
for (const agentId of this.subagentWindows.keys()) {
|
||||
const agent = this.subagents.get(agentId);
|
||||
// Check both subagent parentSessionId and subagentParentMap
|
||||
// (standalone pane windows use subagentParentMap, not subagents map)
|
||||
@@ -2316,19 +2311,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
minimizeSubagentWindow(agentId) {
|
||||
const windowData = this.subagentWindows.get(agentId);
|
||||
if (windowData) {
|
||||
// Dispose teammate terminal on minimize to free DOM/memory (lazy re-creation on restore)
|
||||
this._disposeTeammateTerminalForMinimize(agentId);
|
||||
windowData.element.style.display = 'none';
|
||||
windowData.minimized = true;
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Debounced wrapper — coalesces rapid subagent events (tool_call, progress,
|
||||
// message) into a single DOM update per 100ms per agent window.
|
||||
scheduleSubagentWindowRender(agentId) {
|
||||
@@ -2500,15 +2482,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// Open windows for all active subagents
|
||||
openAllActiveSubagentWindows() {
|
||||
for (const [agentId, agent] of this.subagents) {
|
||||
if (agent.status === 'active' && !this.subagentWindows.has(agentId)) {
|
||||
this.openSubagentWindow(agentId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Agent Teams
|
||||
@@ -2649,155 +2622,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
});
|
||||
},
|
||||
|
||||
/** Open a standalone terminal window for a tmux-pane teammate (no subagent entry needed) */
|
||||
openTeammateTerminalWindow(paneData) {
|
||||
// Only open if the session has a tab in Codeman
|
||||
if (!this.sessions.has(paneData.sessionId)) return;
|
||||
|
||||
// Use pane target as the unique ID for this window
|
||||
const windowId = `pane-${paneData.paneTarget}`;
|
||||
|
||||
// If window already exists, focus it
|
||||
if (this.subagentWindows.has(windowId)) {
|
||||
const existing = this.subagentWindows.get(windowId);
|
||||
if (existing.hidden) {
|
||||
existing.element.style.display = 'flex';
|
||||
existing.hidden = false;
|
||||
}
|
||||
existing.element.style.zIndex = ++this.subagentWindowZIndex;
|
||||
if (existing.minimized) {
|
||||
this.restoreSubagentWindow(windowId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate position
|
||||
const windowCount = this.subagentWindows.size;
|
||||
const windowWidth = 550;
|
||||
const windowHeight = 400;
|
||||
const gap = 20;
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const startX = 50;
|
||||
const startY = 120;
|
||||
const 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;
|
||||
let finalX = startX + col * (windowWidth + gap);
|
||||
let finalY = startY + row * (windowHeight + gap);
|
||||
finalX = Math.max(10, Math.min(finalX, viewportWidth - windowWidth - 10));
|
||||
finalY = Math.max(10, Math.min(finalY, viewportHeight - windowHeight - 10));
|
||||
|
||||
// Color badge
|
||||
const colorClass = paneData.color || 'blue';
|
||||
|
||||
// Create window element
|
||||
const win = document.createElement('div');
|
||||
win.className = 'subagent-window has-terminal';
|
||||
win.id = `subagent-window-${windowId}`;
|
||||
win.style.zIndex = ++this.subagentWindowZIndex;
|
||||
win.style.left = `${finalX}px`;
|
||||
win.style.top = `${finalY}px`;
|
||||
win.style.width = `${windowWidth}px`;
|
||||
win.style.height = `${windowHeight}px`;
|
||||
win.innerHTML = `
|
||||
<div class="subagent-window-header">
|
||||
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(paneData.teammateName)} (pane ${paneData.paneTarget})">
|
||||
<span class="icon" style="color: var(--team-color-${colorClass}, #339af0)">⬤</span>
|
||||
<span class="id">${escapeHtml(paneData.teammateName)}</span>
|
||||
<span class="status running">terminal</span>
|
||||
</div>
|
||||
<div class="subagent-window-actions">
|
||||
<button onclick="app.closeSubagentWindow(${escapeHtml(JSON.stringify(windowId))})" title="Minimize to tab">─</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subagent-window-body teammate-terminal-body" id="subagent-window-body-${windowId}">
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(win);
|
||||
|
||||
// Make draggable
|
||||
const dragListeners = this.makeWindowDraggable(win, win.querySelector('.subagent-window-header'));
|
||||
|
||||
// Make resizable if method exists
|
||||
if (typeof this.makeWindowResizable === 'function') {
|
||||
this.makeWindowResizable(win);
|
||||
}
|
||||
|
||||
// Check visibility based on active session
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
const activeTabOnly = settings.subagentActiveTabOnly ?? true;
|
||||
const shouldHide = activeTabOnly && paneData.sessionId !== this.activeSessionId;
|
||||
|
||||
// Store reference
|
||||
this.subagentWindows.set(windowId, {
|
||||
element: win,
|
||||
minimized: false,
|
||||
hidden: shouldHide,
|
||||
dragListeners,
|
||||
description: `Teammate: ${paneData.teammateName}`,
|
||||
});
|
||||
|
||||
// Also add to subagentParentMap for tab-based visibility
|
||||
this.subagentParentMap.set(windowId, paneData.sessionId);
|
||||
|
||||
if (shouldHide) {
|
||||
win.style.display = 'none';
|
||||
}
|
||||
|
||||
// Focus on click
|
||||
win.addEventListener('mousedown', () => {
|
||||
win.style.zIndex = ++this.subagentWindowZIndex;
|
||||
});
|
||||
|
||||
// Resize observer for connection lines
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
this.updateConnectionLines();
|
||||
});
|
||||
resizeObserver.observe(win);
|
||||
this.subagentWindows.get(windowId).resizeObserver = resizeObserver;
|
||||
|
||||
// Init the xterm.js terminal (lazy if hidden)
|
||||
if (shouldHide) {
|
||||
// Window starts hidden — defer terminal creation until visible (lazy init)
|
||||
const windowEntry = this.subagentWindows.get(windowId);
|
||||
if (windowEntry) {
|
||||
windowEntry._lazyTerminal = true;
|
||||
windowEntry._lazyPaneTarget = paneData.paneTarget;
|
||||
windowEntry._lazySessionId = paneData.sessionId;
|
||||
}
|
||||
} else {
|
||||
this.initTeammateTerminal(windowId, paneData, win);
|
||||
}
|
||||
|
||||
// Animate in
|
||||
requestAnimationFrame(() => {
|
||||
win.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
|
||||
win.style.transform = 'scale(1)';
|
||||
win.style.opacity = '1';
|
||||
});
|
||||
},
|
||||
|
||||
/** Rebuild the teammate lookup map from all team configs */
|
||||
rebuildTeammateMap() {
|
||||
this.teammateMap.clear();
|
||||
for (const [teamName, team] of this.teams) {
|
||||
for (const member of team.members) {
|
||||
if (member.agentType !== 'team-lead') {
|
||||
// Use name as key prefix for matching subagent descriptions
|
||||
this.teammateMap.set(member.name, {
|
||||
name: member.name,
|
||||
color: member.color || 'blue',
|
||||
teamName,
|
||||
agentId: member.agentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** Check if a subagent is a teammate and return its info */
|
||||
getTeammateInfo(agent) {
|
||||
if (!agent?.description) return null;
|
||||
@@ -2807,7 +2631,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const teammateId = match[1];
|
||||
// Extract name from teammate_id (format: name@teamName)
|
||||
const name = teammateId.split('@')[0];
|
||||
return this.teammateMap.get(name) || { name, color: 'blue', teamName: 'unknown' };
|
||||
return { name, color: 'blue', teamName: 'unknown' };
|
||||
},
|
||||
|
||||
/** Get teammate badge HTML for a subagent */
|
||||
@@ -2817,117 +2641,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
return `<span class="teammate-badge teammate-color-${info.color}" title="Team: ${escapeHtml(info.teamName)}">@${escapeHtml(info.name)}</span>`;
|
||||
},
|
||||
|
||||
/** Render the team tasks panel */
|
||||
renderTeamTasksPanel() {
|
||||
const panel = document.getElementById('teamTasksPanel');
|
||||
if (!panel) return;
|
||||
|
||||
// Find team for active session
|
||||
let activeTeam = null;
|
||||
let activeTeamName = null;
|
||||
if (this.activeSessionId) {
|
||||
for (const [name, team] of this.teams) {
|
||||
if (team.leadSessionId === this.activeSessionId) {
|
||||
activeTeam = team;
|
||||
activeTeamName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!activeTeam) {
|
||||
panel.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Set initial position and make draggable on first show
|
||||
const wasHidden = panel.style.display === 'none';
|
||||
panel.style.display = 'flex';
|
||||
|
||||
if (wasHidden && !this.teamTasksDragListeners) {
|
||||
// Position bottom-right
|
||||
const panelWidth = 360;
|
||||
const panelHeight = 300;
|
||||
panel.style.left = `${Math.max(10, window.innerWidth - panelWidth - 20)}px`;
|
||||
panel.style.top = `${Math.max(10, window.innerHeight - panelHeight - 70)}px`;
|
||||
// Make draggable
|
||||
const header = panel.querySelector('.team-tasks-header');
|
||||
if (header) {
|
||||
this.teamTasksDragListeners = this.makeWindowDraggable(panel, header);
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = this.teamTasks.get(activeTeamName) || [];
|
||||
const completed = tasks.filter(t => t.status === 'completed').length;
|
||||
const total = tasks.length;
|
||||
const pct = total > 0 ? Math.round((completed / total) * 100) : 0;
|
||||
|
||||
const headerEl = panel.querySelector('.team-tasks-header-text');
|
||||
if (headerEl) {
|
||||
const teammateCount = activeTeam.members.filter(m => m.agentType !== 'team-lead').length;
|
||||
headerEl.textContent = `Team Tasks (${teammateCount} teammates)`;
|
||||
}
|
||||
|
||||
const progressEl = panel.querySelector('.team-tasks-progress-fill');
|
||||
if (progressEl) {
|
||||
progressEl.style.width = `${pct}%`;
|
||||
}
|
||||
|
||||
const progressText = panel.querySelector('.team-tasks-progress-text');
|
||||
if (progressText) {
|
||||
progressText.textContent = `${completed}/${total}`;
|
||||
}
|
||||
|
||||
const listEl = panel.querySelector('.team-tasks-list');
|
||||
if (!listEl) return;
|
||||
|
||||
if (tasks.length === 0) {
|
||||
listEl.innerHTML = '<div class="team-task-empty">No tasks yet</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const html = tasks.map(task => {
|
||||
const statusIcon = task.status === 'completed' ? '✓' : task.status === 'in_progress' ? '◉' : '○';
|
||||
const statusClass = task.status.replace('_', '-');
|
||||
const ownerBadge = task.owner
|
||||
? `<span class="team-task-owner teammate-color-${this.getTeammateColor(task.owner)}">${escapeHtml(task.owner)}</span>`
|
||||
: '';
|
||||
return `<div class="team-task-item ${statusClass}">
|
||||
<span class="team-task-status">${statusIcon}</span>
|
||||
<span class="team-task-subject">${escapeHtml(task.subject)}</span>
|
||||
${ownerBadge}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
listEl.innerHTML = html;
|
||||
},
|
||||
|
||||
/** Hide team tasks panel and clean up drag listeners */
|
||||
hideTeamTasksPanel() {
|
||||
const panel = document.getElementById('teamTasksPanel');
|
||||
if (panel) panel.style.display = 'none';
|
||||
if (this.teamTasksDragListeners) {
|
||||
document.removeEventListener('mousemove', this.teamTasksDragListeners.move);
|
||||
document.removeEventListener('mouseup', this.teamTasksDragListeners.up);
|
||||
if (this.teamTasksDragListeners.touchMove) {
|
||||
document.removeEventListener('touchmove', this.teamTasksDragListeners.touchMove);
|
||||
document.removeEventListener('touchend', this.teamTasksDragListeners.up);
|
||||
document.removeEventListener('touchcancel', this.teamTasksDragListeners.up);
|
||||
}
|
||||
if (this.teamTasksDragListeners.handle) {
|
||||
this.teamTasksDragListeners.handle.removeEventListener('mousedown', this.teamTasksDragListeners.handleMouseDown);
|
||||
this.teamTasksDragListeners.handle.removeEventListener('touchstart', this.teamTasksDragListeners.handleTouchStart);
|
||||
}
|
||||
this.teamTasksDragListeners = null;
|
||||
}
|
||||
},
|
||||
|
||||
/** Get teammate color by name */
|
||||
getTeammateColor(name) {
|
||||
const info = this.teammateMap.get(name);
|
||||
return info?.color || 'blue';
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Project Insights Panel (Bash Tools with Clickable File Paths)
|
||||
@@ -3038,37 +2751,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pick the "better" of two paths that resolve to the same file.
|
||||
* Prefers paths inside the case folder, longer/more explicit paths, and absolute paths.
|
||||
*/
|
||||
pickBetterPath(path1, path2, workingDir) {
|
||||
// Prefer paths inside the case folder (working directory)
|
||||
if (workingDir) {
|
||||
const inWorkDir1 = this.isPathInWorkingDir(path1, workingDir);
|
||||
const inWorkDir2 = this.isPathInWorkingDir(path2, workingDir);
|
||||
if (inWorkDir1 && !inWorkDir2) return path1;
|
||||
if (inWorkDir2 && !inWorkDir1) return path2;
|
||||
}
|
||||
|
||||
// Prefer absolute paths
|
||||
const abs1 = path1.startsWith('/');
|
||||
const abs2 = path2.startsWith('/');
|
||||
if (abs1 && !abs2) return path1;
|
||||
if (abs2 && !abs1) return path2;
|
||||
|
||||
// Both absolute or both relative - prefer longer (more explicit)
|
||||
if (path1.length !== path2.length) {
|
||||
return path1.length > path2.length ? path1 : path2;
|
||||
}
|
||||
|
||||
// Prefer paths without ~
|
||||
if (!path1.includes('~') && path2.includes('~')) return path1;
|
||||
if (!path2.includes('~') && path1.includes('~')) return path2;
|
||||
|
||||
return path1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deduplicate file paths across all tools, keeping the "best" version.
|
||||
* Uses smart equivalence checking:
|
||||
|
||||
@@ -953,11 +953,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// Legacy method for backwards compatibility
|
||||
getTodoIcon(status) {
|
||||
return this.getRalphTaskIcon(status);
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Plan Versioning
|
||||
|
||||
@@ -731,20 +731,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
this.showToast(`Saved preset: ${name}`, 'success');
|
||||
},
|
||||
|
||||
deletePreset(presetId) {
|
||||
const presets = this.loadRespawnPresets();
|
||||
const preset = presets.find(p => p.id === presetId);
|
||||
if (!preset || preset.builtIn) {
|
||||
this.showToast('Cannot delete built-in presets', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = presets.filter(p => p.id !== presetId);
|
||||
this.saveRespawnPresets(filtered);
|
||||
this.renderPresetDropdown();
|
||||
this.showToast(`Deleted preset: ${preset.name}`, 'success');
|
||||
},
|
||||
|
||||
// Get respawn config from modal inputs
|
||||
getModalRespawnConfig() {
|
||||
const updatePrompt = document.getElementById('modalRespawnPrompt').value;
|
||||
@@ -947,13 +933,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
await this.loadRunSummary(sessionId);
|
||||
},
|
||||
|
||||
closeRunSummary() {
|
||||
this.runSummarySessionId = null;
|
||||
this.stopRunSummaryAutoRefresh();
|
||||
// Close session options modal (summary is now a tab in it)
|
||||
this.closeSessionOptions();
|
||||
},
|
||||
|
||||
async refreshRunSummary() {
|
||||
const sessionId = this.runSummarySessionId || this.editingSessionId;
|
||||
if (!sessionId) return;
|
||||
@@ -1074,7 +1053,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
renderRunSummary() {
|
||||
if (!this.runSummaryData) return;
|
||||
|
||||
const { stats, events, sessionName, startedAt, lastUpdatedAt } = this.runSummaryData;
|
||||
const { events, sessionName, startedAt, lastUpdatedAt } = this.runSummaryData;
|
||||
|
||||
// Update session info
|
||||
const duration = lastUpdatedAt - startedAt;
|
||||
@@ -1175,10 +1154,4 @@ Object.assign(CodemanApp.prototype, {
|
||||
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
||||
return `${seconds}s`;
|
||||
},
|
||||
|
||||
saveSessionOptions() {
|
||||
// Session options are applied immediately via individual controls
|
||||
// This just closes the modal
|
||||
this.closeSessionOptions();
|
||||
},
|
||||
});
|
||||
@@ -21,13 +21,12 @@
|
||||
* own no-id-branching rule exists to prevent server-side.
|
||||
*
|
||||
* Deliberately a LOCAL table rather than a server-injected catalogue: several
|
||||
* unit tests exercise these run*() methods inside a bare `vm.createContext()`
|
||||
* unit tests exercise the launcher inside a bare `vm.createContext()`
|
||||
* sandbox with no `window` global at all (see test/run-mode-ui.test.ts) —
|
||||
* referencing `window` there unguarded would throw, not degrade. `buildConfig`
|
||||
* returns the CLI's top-level legacy config field for a LOCAL launch, or
|
||||
* `null` for a CLI that sends none (pi: no bypass flag exists, so there is
|
||||
* nothing to send — see runPi's own history below for why that must stay
|
||||
* true).
|
||||
* nothing to send, and that must stay true).
|
||||
*/
|
||||
const RUN_MODE_LAUNCH = {
|
||||
opencode: {
|
||||
@@ -677,10 +676,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
return this._caseSyncChain;
|
||||
},
|
||||
|
||||
async quickStart() {
|
||||
return this.run();
|
||||
},
|
||||
|
||||
/** Ensure a newly-created session is visible without waiting for the SSE event.
|
||||
* The POST response and session:created can arrive in either order, so the
|
||||
* normal idempotent SSE handler remains the single state-upsert path.
|
||||
@@ -2484,7 +2479,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
if (remoteIds[0]) {
|
||||
// Don't pre-set activeSessionId — selectSession early-returns when the
|
||||
// IDs match, skipping the buffer load, tab activation, and focus (see runCodex).
|
||||
// IDs match, skipping the buffer load, tab activation, and focus (see _runCliMode).
|
||||
await this.selectSession(remoteIds[0]);
|
||||
}
|
||||
this.terminal.focus();
|
||||
@@ -2536,7 +2531,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Switch to first session. Don't pre-set activeSessionId — selectSession
|
||||
// early-returns when the IDs match, skipping the buffer load, tab
|
||||
// activation, and focus (see runCodex), which left the new shell tab
|
||||
// activation, and focus (see _runCliMode), which left the new shell tab
|
||||
// created but not shown until the user manually clicked it.
|
||||
if (sessionIds.length > 0) {
|
||||
await this.selectSession(sessionIds[0]);
|
||||
@@ -2601,10 +2596,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
* Shared launcher for every RUN_MODE_LAUNCH entry (every run mode except
|
||||
* claude/shell, which have their own flows — claude for its remote/docker
|
||||
* branching and parallel-create path, shell for needing no CLI probe at
|
||||
* all). The eight run<Mode>() methods below are thin named wrappers: their
|
||||
* names stay because index.html's welcome-screen buttons and the run-mode
|
||||
* menu call them directly by name (`app.runOpenCode()` etc.), and several
|
||||
* tests assert on that name directly too.
|
||||
* all). `run()` reaches it for every mode in the table; there is no per-CLI
|
||||
* wrapper method any more.
|
||||
*/
|
||||
async _runCliMode(mode, options = {}) {
|
||||
const catalogEntry = registryCliById(mode);
|
||||
@@ -2690,46 +2683,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
async runOpenCode() {
|
||||
return this._runCliMode('opencode');
|
||||
},
|
||||
|
||||
async runCodex() {
|
||||
return this._runCliMode('codex');
|
||||
},
|
||||
|
||||
async runGemini() {
|
||||
return this._runCliMode('gemini');
|
||||
},
|
||||
|
||||
async runAntigravity() {
|
||||
return this._runCliMode('antigravity');
|
||||
},
|
||||
|
||||
async runPi() {
|
||||
return this._runCliMode('pi');
|
||||
},
|
||||
|
||||
async runOmp() {
|
||||
return this._runCliMode('omp');
|
||||
},
|
||||
|
||||
async runGrok() {
|
||||
return this._runCliMode('grok');
|
||||
},
|
||||
|
||||
async runDeepSeek() {
|
||||
return this._runCliMode('deepseek');
|
||||
},
|
||||
|
||||
async runCopilot() {
|
||||
return this._runCliMode('copilot');
|
||||
},
|
||||
|
||||
async runJcode() {
|
||||
return this._runCliMode('jcode');
|
||||
},
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Session Options Modal
|
||||
|
||||
@@ -5455,14 +5455,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the parent session ID for an agent from the persistent map.
|
||||
* This is the ONLY source of truth for connection lines.
|
||||
*/
|
||||
getAgentParentSessionId(agentId) {
|
||||
return this.subagentParentMap.get(agentId) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set and persist the parent session ID for an agent.
|
||||
* Once set, this association is PERMANENT and never recalculated.
|
||||
|
||||
@@ -329,7 +329,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
box-shadow: var(--elevated-shadow);
|
||||
}
|
||||
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(
|
||||
.settings-item,
|
||||
.cron-job-row,
|
||||
#cronModal .form-row-switch.cron-switch-row,
|
||||
.cron-weekdays label
|
||||
@@ -338,7 +337,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
border-color: var(--control-border);
|
||||
}
|
||||
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(
|
||||
.settings-item:hover,
|
||||
.cron-job-row:hover,
|
||||
.cron-weekdays label:hover
|
||||
) {
|
||||
@@ -418,7 +416,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
/* Button focus styles */
|
||||
button:focus-visible,
|
||||
.btn-toolbar:focus-visible,
|
||||
.btn-icon:focus-visible,
|
||||
.btn-icon-header:focus-visible,
|
||||
.btn-icon-sm:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
@@ -2707,13 +2704,6 @@ html[data-header-stats] .header-system-stats .stat-item[hidden] {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
.tunnel-panel-empty {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.header-tokens {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
@@ -3757,16 +3747,6 @@ html[data-tab-logos='off'] .home-sessions-harness {
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.ralph-ring-progress {
|
||||
fill: none;
|
||||
stroke: url(#ralphGradient);
|
||||
stroke-width: 3;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 100;
|
||||
stroke-dashoffset: 100;
|
||||
transition: stroke-dashoffset 0.5s ease-out;
|
||||
}
|
||||
|
||||
.ralph-ring-progress-mini {
|
||||
fill: none;
|
||||
stroke: url(#ralphGradientMini);
|
||||
@@ -4086,23 +4066,6 @@ html[data-tab-logos='off'] .home-sessions-harness {
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
.ralph-tasks-compact .ralph-task-card {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.65rem;
|
||||
border-left-width: 2px;
|
||||
}
|
||||
|
||||
.ralph-tasks-compact .ralph-task-icon {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.ralph-tasks-compact .ralph-task-content {
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Large Progress Ring */
|
||||
.ralph-ring-svg-large {
|
||||
width: 100%;
|
||||
@@ -6405,11 +6368,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
}
|
||||
|
||||
/* Button icon styling */
|
||||
.btn-toolbar .btn-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-toolbar.btn-success {
|
||||
background: linear-gradient(135deg, #16a34a 0%, #22c55e 100%);
|
||||
@@ -7153,28 +7111,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.toolbar-input-sm {
|
||||
padding: 0.3rem 0.4rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.75rem;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar-input-sm:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.toolbar-input-sm::-webkit-inner-spin-button,
|
||||
.toolbar-input-sm::-webkit-outer-spin-button {
|
||||
opacity: 1;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
#dirDisplay {
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
@@ -7231,18 +7167,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 1.25rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.settings-section:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
@@ -7296,19 +7220,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
.form-col {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-col label {
|
||||
display: block;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 0.35rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.input-suffix {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -7352,46 +7263,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Preset selector */
|
||||
.preset-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preset-selector select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0.4rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.preset-selector select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.preset-selector .btn-sm {
|
||||
white-space: nowrap;
|
||||
padding: 4px 8px;
|
||||
font-size: 0.7rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.preset-selector .btn-sm:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--border-light);
|
||||
}
|
||||
|
||||
/* Switch toggle for form rows */
|
||||
.form-section-header {
|
||||
font-size: 0.7rem;
|
||||
@@ -7495,138 +7366,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
transform: translateX(12px);
|
||||
}
|
||||
|
||||
/* Settings Grid Layout - 2 column compact view */
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
/* minmax(0, 1fr) lets tracks shrink below their min-content so long
|
||||
labels ellipsis-truncate instead of blowing out the modal width and
|
||||
forcing a horizontal scrollbar (1fr === minmax(auto, 1fr) would not). */
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.settings-section-header {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 0.65rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-muted);
|
||||
padding: 0.5rem 0 0.25rem 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.settings-section-header:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.45rem 0.7rem;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: var(--btn-radius);
|
||||
font-size: 0.75rem;
|
||||
transition: background var(--transition-smooth);
|
||||
}
|
||||
|
||||
.settings-item-label {
|
||||
color: var(--text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.settings-inline-input,
|
||||
.settings-inline-select {
|
||||
width: min(190px, 52%);
|
||||
min-width: 0;
|
||||
padding: 0.3rem 0.45rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.settings-inline-input:focus,
|
||||
.settings-inline-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Skin picker: full-width row (not squeezed into a half-width grid cell) with an
|
||||
auto-width select, so the "Skin" label keeps its space instead of truncating. */
|
||||
.settings-item-skin {
|
||||
grid-column: 1 / -1;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.settings-item-skin .settings-item-label {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.settings-item-skin .form-select {
|
||||
width: auto;
|
||||
min-width: 220px;
|
||||
max-width: 320px;
|
||||
flex: 0 1 auto;
|
||||
/* Match the field to its settings row: the base .form-select (0.8rem / 0.5rem
|
||||
padding) renders bigger and taller than the row, and the daylight skins'
|
||||
Manrope font exaggerates it further. Slim it to the row's 0.75rem text. */
|
||||
font-size: 0.75rem;
|
||||
padding-top: 0.3rem;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.settings-item-multiline {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-item-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.settings-item-desc {
|
||||
font-size: 0.6rem;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.settings-item-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Three column variant for notification levels */
|
||||
.settings-grid-3col {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
/* Inline input in settings item */
|
||||
.settings-item-input {
|
||||
width: 60px;
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
color: var(--text);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.settings-item-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Status badge in settings */
|
||||
.settings-status {
|
||||
font-size: 0.6rem;
|
||||
@@ -8310,13 +8049,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
.run-summary-session-info {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.run-summary-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -8363,19 +8095,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
}
|
||||
|
||||
/* Legacy run summary modal footer */
|
||||
#runSummaryModal .modal-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
#runSummaryModal .modal-footer .run-summary-session-info {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
|
||||
.modal-warning-text {
|
||||
color: var(--text-dim);
|
||||
@@ -8697,10 +8416,6 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
}
|
||||
|
||||
/* Session respawn section in options modal */
|
||||
.session-respawn-section {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.session-respawn-status {
|
||||
display: flex;
|
||||
@@ -8814,86 +8529,7 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
/* Respawn status + Enable/Stop row — sits at the bottom of the Respawn tab,
|
||||
below the settings it acts on */
|
||||
.respawn-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid rgba(34, 197, 94, 0.2);
|
||||
}
|
||||
|
||||
.respawn-header .respawn-actions {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Respawn options row - inline checkboxes */
|
||||
.respawn-options-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
/* Auto-resume on usage limit (token pause control) */
|
||||
.auto-resume-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.3rem;
|
||||
margin: 0.75rem 0;
|
||||
padding: 0.6rem 0.75rem;
|
||||
background: rgba(59, 130, 246, 0.07);
|
||||
border: 1px solid rgba(59, 130, 246, 0.35);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.auto-resume-box .checkbox-inline {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.auto-resume-box .form-hint {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Respawn loop box — groups every setting that belongs to the autonomous
|
||||
respawn cycle (duration, presets, cycle prompts, enable). Green like its
|
||||
Enable button; a visual sibling of the standalone auto-resume option above */
|
||||
.respawn-loop-box {
|
||||
margin: 0.75rem 0;
|
||||
padding: 0.6rem 0.75rem;
|
||||
background: rgba(34, 197, 94, 0.05);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.respawn-loop-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.respawn-loop-hint {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.respawn-loop-box .form-section-header {
|
||||
border-bottom-color: rgba(34, 197, 94, 0.2);
|
||||
}
|
||||
|
||||
/* Match the cycle-step checkboxes (2./3.) to the step labels (1./4.) */
|
||||
.respawn-loop-box .checkbox-inline {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.auto-resume-status {
|
||||
display: none;
|
||||
@@ -8906,106 +8542,11 @@ html[data-skin="og"] .welcome-primary:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Respawn actions in modal */
|
||||
.respawn-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* The `.modal-tabs` / `.modal-tab-btn` / `.modal-tab-content` chrome is gone:
|
||||
App Settings, Session Options and Add Case all use the `set-*` surface now.
|
||||
Its last user was Add Case; see the settings-surface block near the end. */
|
||||
|
||||
/* Context Tab Settings Grid */
|
||||
.context-settings-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.context-setting {
|
||||
padding: 0.875rem;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.context-setting-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.625rem;
|
||||
}
|
||||
|
||||
.context-setting-header .checkbox-inline span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.context-setting-header .input-suffix-sm {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.context-setting-header .input-suffix-sm input {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.context-setting input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 0.375rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.context-setting input[type="text"]:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.context-setting .form-hint {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Ralph Limits Grid */
|
||||
.ralph-limits-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.ralph-limits-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.ralph-limits-grid .form-col input[type="number"] {
|
||||
width: 100%;
|
||||
padding: 0.375rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.ralph-limits-grid .form-col .form-hint {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
/* Ralph Config Actions */
|
||||
.ralph-config-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
@@ -10641,9 +10182,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
}
|
||||
|
||||
/* Header removed - no longer needed */
|
||||
.subagent-dropdown-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.subagent-dropdown-item {
|
||||
display: flex;
|
||||
@@ -10723,13 +10261,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
transition: opacity 0.1s, color 0.1s;
|
||||
}
|
||||
|
||||
.task-empty {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Task Tree */
|
||||
.task-tree {
|
||||
font-size: 0.75rem;
|
||||
@@ -10810,28 +10341,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Task toggle button in toolbar */
|
||||
.task-toggle-btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.task-toggle-btn .badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
min-width: 14px;
|
||||
height: 14px;
|
||||
padding: 0 3px;
|
||||
background: var(--yellow);
|
||||
border-radius: 7px;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
@@ -10843,13 +10352,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
background: var(--border-light);
|
||||
}
|
||||
|
||||
.process-empty {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.process-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -11833,11 +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));
|
||||
}
|
||||
|
||||
.connection-line.spawning-line {
|
||||
stroke-dasharray: 1000;
|
||||
stroke-dashoffset: 1000;
|
||||
}
|
||||
|
||||
/* Wizard connection lines - different color to show plan generation link */
|
||||
.connection-line.wizard-connection {
|
||||
stroke: #60a5fa;
|
||||
@@ -11971,10 +11468,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
.connection-line.lineage-line--working {
|
||||
animation: lineage-flow 1.1s linear infinite;
|
||||
}
|
||||
|
||||
.lineage-line-dot--working {
|
||||
animation: lineage-dot-pulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
/* Two full dash cycles, so the march loops seamlessly. Tied to the working
|
||||
@@ -12341,10 +11834,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
.file-tree-item.hidden-by-filter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Under an expanded folder the scan's file limit stopped short of. Inert: no hover, no click. */
|
||||
.file-tree-incomplete {
|
||||
padding: 0.2rem 0.5rem;
|
||||
@@ -12992,12 +12481,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.log-viewer-window-body .log-line {
|
||||
padding: 0.1rem 0;
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: auto 20px;
|
||||
}
|
||||
|
||||
.log-viewer-window-body .log-error {
|
||||
color: var(--error);
|
||||
}
|
||||
@@ -13544,60 +13027,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
}
|
||||
|
||||
/* Existing wizard files section */
|
||||
.existing-wizard-files-section {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.existing-wizard-card {
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.existing-wizard-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.existing-wizard-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.existing-wizard-title {
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.existing-wizard-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-dim);
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.existing-wizard-agents {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.existing-wizard-agent {
|
||||
background: rgba(96, 165, 250, 0.2);
|
||||
color: var(--accent);
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.existing-wizard-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plan-cancel-btn {
|
||||
color: var(--red);
|
||||
@@ -13740,22 +13169,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Clickable prompt link in plan subagent header */
|
||||
.plan-subagent-prompt-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.15rem 0.3rem;
|
||||
margin: -0.15rem -0.3rem;
|
||||
border-radius: 4px;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.plan-subagent-prompt-link:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Clickable status text */
|
||||
.plan-subagent-status-text.clickable {
|
||||
cursor: pointer;
|
||||
@@ -13823,11 +13236,6 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
}
|
||||
|
||||
/* Iteration Presets */
|
||||
.iteration-presets {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.iteration-preset-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
@@ -16771,20 +16179,6 @@ body.touch-device.cjk-input-visible .main {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.attachment-type-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 6px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border-light);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.attachment-card-main {
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -17129,7 +16523,6 @@ html:not([data-skin="og"]) {
|
||||
background: linear-gradient(90deg, var(--banner-bg-a), var(--banner-bg-b));
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
.respawn-countdown-timer .respawn-timer-value,
|
||||
.action-type { color: var(--accent); }
|
||||
.respawn-timer-progress { background: var(--accent); }
|
||||
.respawn-action-log { border-left-color: rgba(var(--accent-rgb), 0.18); }
|
||||
@@ -17145,7 +16538,6 @@ html:not([data-skin="og"]) {
|
||||
/* Ralph status badges: collapse the off-token Material palette onto the system */
|
||||
.ralph-status-badge { color: var(--text-dim); }
|
||||
.ralph-status-dot { box-shadow: none; }
|
||||
.ralph-info { background: rgba(var(--accent-rgb), 0.05); }
|
||||
|
||||
/* ---- Bottom toolbar + neutral buttons ---- */
|
||||
.toolbar { box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.22), 0 -4px 14px rgba(0, 0, 0, 0.14); }
|
||||
@@ -17312,10 +16704,6 @@ html:not([data-skin="og"]) {
|
||||
border: 1px solid var(--control-border);
|
||||
}
|
||||
|
||||
/* ---- Subagent progress ring: emerald, calm glow ---- */
|
||||
.subagent-ring-progress,
|
||||
.subagent-ring circle.progress { stroke: var(--accent); }
|
||||
|
||||
/* ---- PROGRESS RING (Ralph large): the one place a soft glow lives ---- */
|
||||
.ralph-ring-fill { filter: drop-shadow(0 0 6px rgba(var(--accent-rgb), 0.45)); }
|
||||
|
||||
|
||||
@@ -939,7 +939,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Clean up ALL floating windows (called during handleInit to prevent memory leaks on reconnect)
|
||||
cleanupAllFloatingWindows() {
|
||||
// Clean up all subagent windows with their ResizeObservers and drag listeners
|
||||
for (const [agentId, windowData] of this.subagentWindows) {
|
||||
for (const windowData of this.subagentWindows.values()) {
|
||||
if (windowData.resizeObserver) {
|
||||
windowData.resizeObserver.disconnect();
|
||||
}
|
||||
@@ -969,7 +969,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
this.teammatePanesByName.clear();
|
||||
|
||||
// Clean up all log viewer windows with their EventSources and drag listeners
|
||||
for (const [windowId, data] of this.logViewerWindows) {
|
||||
for (const data of this.logViewerWindows.values()) {
|
||||
if (data.eventSource) {
|
||||
data.eventSource.close();
|
||||
}
|
||||
@@ -988,7 +988,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Clean up plan subagent windows (wizard agents)
|
||||
if (this.planSubagents) {
|
||||
for (const [agentId, windowData] of this.planSubagents) {
|
||||
for (const windowData of this.planSubagents.values()) {
|
||||
if (windowData.dragListeners) {
|
||||
document.removeEventListener('mousemove', windowData.dragListeners.move);
|
||||
document.removeEventListener('mouseup', windowData.dragListeners.up);
|
||||
@@ -1001,7 +1001,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
// Clean up all image popup windows with their drag listeners
|
||||
for (const [imageId, popupData] of this.imagePopups) {
|
||||
for (const popupData of this.imagePopups.values()) {
|
||||
if (popupData.dragListeners) {
|
||||
document.removeEventListener('mousemove', popupData.dragListeners.move);
|
||||
document.removeEventListener('mouseup', popupData.dragListeners.up);
|
||||
@@ -1042,18 +1042,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
this.activeFocusTrap = null;
|
||||
}
|
||||
|
||||
// Clean up team tasks panel drag listeners
|
||||
if (this.teamTasksDragListeners) {
|
||||
document.removeEventListener('mousemove', this.teamTasksDragListeners.move);
|
||||
document.removeEventListener('mouseup', this.teamTasksDragListeners.up);
|
||||
if (this.teamTasksDragListeners.touchMove) {
|
||||
document.removeEventListener('touchmove', this.teamTasksDragListeners.touchMove);
|
||||
document.removeEventListener('touchend', this.teamTasksDragListeners.up);
|
||||
document.removeEventListener('touchcancel', this.teamTasksDragListeners.up);
|
||||
}
|
||||
this.teamTasksDragListeners = null;
|
||||
}
|
||||
|
||||
// Clear minimized agents tracking
|
||||
this.minimizedSubagents.clear();
|
||||
|
||||
|
||||
@@ -1256,7 +1256,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
let touchLastX = 0;
|
||||
let touchLastY = 0;
|
||||
let velocity = 0;
|
||||
let lastTime = 0;
|
||||
let scrollFrame = null;
|
||||
let isTouching = false;
|
||||
// One id per touch contact (touchstart), carried by its touchmoves AND its
|
||||
@@ -1264,10 +1263,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// gesture however slowly the finger moves (pageKeysForGesture).
|
||||
let touchContactId = 0;
|
||||
|
||||
const scrollLoop = (timestamp) => {
|
||||
const dt = lastTime ? (timestamp - lastTime) / 16.67 : 1;
|
||||
lastTime = timestamp;
|
||||
|
||||
const scrollLoop = () => {
|
||||
if (!isTouching && Math.abs(velocity) > 0.3) {
|
||||
// Momentum phase — convert pixel velocity to lines
|
||||
const lines = Math.round(velocity / cellHeight());
|
||||
@@ -1389,7 +1385,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
longPressTimer = null;
|
||||
this._beginTouchSelection(longPressStartX, longPressStartY);
|
||||
}, window.CodemanTerminalInput.TOUCH_SELECT_LONG_PRESS_MS);
|
||||
lastTime = 0;
|
||||
if (scrollFrame) {
|
||||
cancelAnimationFrame(scrollFrame);
|
||||
scrollFrame = null;
|
||||
|
||||
@@ -409,17 +409,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
this.updateConnectionLines();
|
||||
},
|
||||
|
||||
// ── Minimize an agent transcript window into its tab (same idiom as run windows) ──
|
||||
|
||||
/** Is this agent transcript currently minimized to a tab? */
|
||||
_isUltracodeAgentMinimized(agentId) {
|
||||
if (!this.minimizedUltracodeAgents) return false;
|
||||
for (const map of this.minimizedUltracodeAgents.values()) {
|
||||
if (map.has(agentId)) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** Look up a minimized agent's {runId,label} entry (across sessions). */
|
||||
_getMinimizedUltracodeAgent(agentId) {
|
||||
if (!this.minimizedUltracodeAgents) return null;
|
||||
|
||||
Reference in new issue
Block a user