mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
- Add SubagentWatcher service that discovers and tails Claude Code's subagent transcript files (~/.claude/projects/.../subagents/agent-*.jsonl) - Track tool calls, progress events, and messages in real-time via file watching - Add SSE events for subagent:discovered, subagent:tool_call, subagent:progress, subagent:message, subagent:completed - Add REST API endpoints: GET /api/subagents, /api/subagents/:id, /api/subagents/:id/transcript - Add subagent panel in Monitor section showing active agents and their activity - Add floating draggable windows that auto-open for each active agent - Add standalone CLI tool: scripts/watch-subagents.ts for terminal monitoring Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
740 lines
38 KiB
HTML
740 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="description" content="Claude Code session manager with web interface">
|
||
<meta name="theme-color" content="#0a0a0a">
|
||
<title>Claudeman</title>
|
||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
|
||
<link rel="stylesheet" href="styles.css">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<!-- Compact Header with Session Tabs -->
|
||
<header class="header">
|
||
<div class="header-brand">
|
||
<span class="logo">Claudeman</span>
|
||
</div>
|
||
|
||
<!-- Session Tabs -->
|
||
<div class="session-tabs" id="sessionTabs">
|
||
</div>
|
||
|
||
<div class="header-right">
|
||
<div class="header-font-controls">
|
||
<button class="btn-icon-header btn-sm" onclick="app.decreaseFontSize()" title="Decrease font (Ctrl+-)">A-</button>
|
||
<span class="font-size-display" id="fontSizeDisplay">14</span>
|
||
<button class="btn-icon-header btn-sm" onclick="app.increaseFontSize()" title="Increase font (Ctrl++)">A+</button>
|
||
</div>
|
||
<div class="header-system-stats" id="headerSystemStats" title="System resource usage">
|
||
<div class="stat-item">
|
||
<span class="stat-label">CPU</span>
|
||
<div class="stat-bar">
|
||
<div class="stat-bar-fill stat-bar-cpu" id="statCpuBar"></div>
|
||
</div>
|
||
<span class="stat-value" id="statCpu">--%</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">MEM</span>
|
||
<div class="stat-bar">
|
||
<div class="stat-bar-fill stat-bar-mem" id="statMemBar"></div>
|
||
</div>
|
||
<span class="stat-value" id="statMem">--</span>
|
||
</div>
|
||
</div>
|
||
<button class="btn-icon-header btn-notifications" onclick="app.toggleNotifications()" title="Notifications">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
||
<span class="notification-badge" id="notifBadge" style="display:none;">0</span>
|
||
</button>
|
||
<button class="btn-icon-header btn-settings" onclick="app.openAppSettings()" title="App Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
||
<div class="header-tokens" id="headerTokens" title="Total tokens across all sessions">0 tokens</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Timer Banner (shown when timed run is active) -->
|
||
<div class="timer-banner" id="timerBanner" style="display: none;">
|
||
<div class="timer-content">
|
||
<span class="timer-value" id="timerValue">00:00:00</span>
|
||
<div class="timer-progress"><div class="timer-progress-fill" id="timerProgress"></div></div>
|
||
<span class="timer-meta" id="timerMeta">0 tasks | $0.00</span>
|
||
</div>
|
||
<button class="btn-icon-only" onclick="app.stopCurrentRun()" title="Stop">■</button>
|
||
</div>
|
||
|
||
<!-- Respawn Banner -->
|
||
<div class="respawn-banner" id="respawnBanner" style="display: none;">
|
||
<div class="respawn-banner-row">
|
||
<span class="respawn-indicator">↻</span>
|
||
<span class="respawn-state" id="respawnState">Watching</span>
|
||
<span class="detection-status" id="detectionStatus"></span>
|
||
<span class="detection-waiting" id="detectionWaiting" style="display: none;"></span>
|
||
<span class="detection-confidence" id="detectionConfidence"></span>
|
||
<span class="detection-ai-check" id="detectionAiCheck" style="display: none;"></span>
|
||
<span class="respawn-spacer"></span>
|
||
<span class="respawn-cycles">#<span id="respawnCycleCount">0</span></span>
|
||
<span class="respawn-timer" id="respawnTimer" style="display: none;"></span>
|
||
<span class="respawn-tokens" id="respawnTokens" style="display: none;"></span>
|
||
<button class="btn-icon-only" onclick="app.stopRespawn()" title="Stop Respawn">■</button>
|
||
</div>
|
||
<div class="respawn-timers-row" id="respawnTimersRow" style="display: none;">
|
||
<div class="respawn-countdown-timers" id="respawnCountdownTimers"></div>
|
||
<div class="respawn-action-log" id="respawnActionLog"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ralph / Todo Tracker Panel -->
|
||
<div class="ralph-panel collapsed" id="ralphStatePanel" style="display: none;">
|
||
<!-- Collapsed Summary Bar -->
|
||
<div class="ralph-summary" id="ralphSummary">
|
||
<div class="ralph-summary-content" onclick="app.toggleRalphStatePanel()">
|
||
<div class="ralph-ring-mini" id="ralphRingMini">
|
||
<svg viewBox="0 0 36 36" class="ralph-ring-svg">
|
||
<defs>
|
||
<linearGradient id="ralphGradientMini" x1="0%" y1="0%" x2="100%" y2="100%">
|
||
<stop offset="0%" stop-color="#3b82f6" />
|
||
<stop offset="100%" stop-color="#22c55e" />
|
||
</linearGradient>
|
||
</defs>
|
||
<circle class="ralph-ring-bg" cx="18" cy="18" r="15.9" />
|
||
<circle class="ralph-ring-progress-mini" cx="18" cy="18" r="15.9" id="ralphRingMiniProgress" />
|
||
</svg>
|
||
<span class="ralph-ring-text" id="ralphRingMiniText">0%</span>
|
||
</div>
|
||
<div class="ralph-summary-info">
|
||
<span class="ralph-title">Ralph / Todo Tracker</span>
|
||
<span class="ralph-status-badge" id="ralphStatusBadge">
|
||
<span class="ralph-status-dot"></span>
|
||
<span class="ralph-status-text">Idle</span>
|
||
</span>
|
||
</div>
|
||
<div class="ralph-summary-stats">
|
||
<span class="ralph-stat"><span class="ralph-stat-icon">⏱</span><span id="ralphStatTime">0m</span></span>
|
||
<span class="ralph-stat"><span class="ralph-stat-icon">✓</span><span id="ralphStatTasks">0/0</span></span>
|
||
</div>
|
||
<span class="ralph-toggle" id="ralphToggle">▼</span>
|
||
</div>
|
||
<div class="ralph-controls">
|
||
<button class="btn-icon-sm" onclick="event.stopPropagation(); app.toggleRalphDetach()" title="Detach/Attach panel" id="ralphDetachBtn">⧉</button>
|
||
<button class="btn-icon-sm" onclick="event.stopPropagation(); app.closeRalphTracker()" title="Close tracker">×</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Expanded Detail View - Compact horizontal layout -->
|
||
<div class="ralph-detail" id="ralphDetail">
|
||
<div class="ralph-detail-row">
|
||
<!-- Progress Ring -->
|
||
<div class="ralph-ring-compact">
|
||
<svg viewBox="0 0 100 100" class="ralph-ring-svg-large">
|
||
<defs>
|
||
<linearGradient id="ralphGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||
<stop offset="0%" stop-color="#3b82f6" />
|
||
<stop offset="100%" stop-color="#22c55e" />
|
||
</linearGradient>
|
||
</defs>
|
||
<circle class="ralph-ring-track" cx="50" cy="50" r="42" />
|
||
<circle class="ralph-ring-fill" cx="50" cy="50" r="42" id="ralphRingProgress" />
|
||
</svg>
|
||
<div class="ralph-ring-center">
|
||
<div class="ralph-ring-percent" id="ralphRingPercent">0%</div>
|
||
<div class="ralph-ring-label">Complete</div>
|
||
</div>
|
||
</div>
|
||
<!-- Meta Info + Tasks unified -->
|
||
<div class="ralph-info-unified">
|
||
<div class="ralph-info-labels">
|
||
<div class="ralph-info-row">
|
||
<span class="ralph-info-label">Tasks</span>
|
||
<span class="ralph-tasks-count" id="ralphTasksCount">0/0</span>
|
||
</div>
|
||
<div class="ralph-info-row">
|
||
<span class="ralph-info-label">Phrase</span>
|
||
<code id="ralphPhrase">--</code>
|
||
</div>
|
||
<div class="ralph-info-row">
|
||
<span class="ralph-info-label">Elapsed</span>
|
||
<span id="ralphElapsed">0m</span>
|
||
</div>
|
||
</div>
|
||
<div class="ralph-tasks-grid" id="ralphTasksGrid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Main Terminal Area -->
|
||
<main class="main">
|
||
<div class="terminal-container" id="terminalContainer"></div>
|
||
</main>
|
||
|
||
<!-- Bottom Toolbar -->
|
||
<footer class="toolbar">
|
||
<div class="toolbar-left">
|
||
<!-- Run Claude -->
|
||
<div class="toolbar-group">
|
||
<button class="btn-toolbar btn-claude" onclick="app.runClaude()" title="Run Claude (Ctrl+Enter)">
|
||
Run Claude
|
||
</button>
|
||
<div class="tab-count-group" title="Instance count">
|
||
<button class="tab-count-btn" onclick="app.decrementTabCount()" title="Instance count">−</button>
|
||
<input type="number" id="tabCount" class="tab-count-input" value="1" min="1" max="20" readonly title="Instance count">
|
||
<button class="tab-count-btn" onclick="app.incrementTabCount()" title="Instance count">+</button>
|
||
</div>
|
||
<button class="btn-toolbar btn-shell" onclick="app.runShell()" title="Run Shell">
|
||
Run Shell
|
||
</button>
|
||
<div class="tab-count-group" title="Instance count">
|
||
<button class="tab-count-btn" onclick="app.decrementShellCount()" title="Instance count">−</button>
|
||
<input type="number" id="shellCount" class="tab-count-input" value="1" min="1" max="20" readonly title="Instance count">
|
||
<button class="tab-count-btn" onclick="app.incrementShellCount()" title="Instance count">+</button>
|
||
</div>
|
||
<div class="case-select-group">
|
||
<select id="quickStartCase" class="toolbar-select" title="Select case">
|
||
<option value="testcase">testcase</option>
|
||
</select>
|
||
<button class="btn-case-add" onclick="app.showCreateCaseModal()" title="Create new case">+</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Directory (hidden) -->
|
||
<div class="toolbar-group" style="display: none;">
|
||
<button class="btn-toolbar" onclick="app.toggleDirInput()" title="Set working directory">
|
||
<span id="dirDisplay">No directory</span>
|
||
</button>
|
||
<input type="text" id="dirInput" class="toolbar-input hidden" placeholder="Working directory..." onblur="app.hideDirInput()">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toolbar-center">
|
||
<span class="version-display" id="versionDisplay" title="Claudeman version">v0.0.0</span>
|
||
</div>
|
||
|
||
<div class="toolbar-right">
|
||
<!-- Buttons removed - use keyboard shortcuts instead -->
|
||
</div>
|
||
</footer>
|
||
|
||
<!-- Help Modal -->
|
||
<div class="modal" id="helpModal">
|
||
<div class="modal-backdrop" onclick="app.closeHelp()"></div>
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Keyboard Shortcuts</h3>
|
||
<button class="modal-close" onclick="app.closeHelp()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="shortcuts-grid">
|
||
<div><kbd>Ctrl</kbd>+<kbd>Enter</kbd></div><div>Run Claude</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>W</kbd></div><div>Close Session</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>Tab</kbd></div><div>Next Session</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>K</kbd></div><div>Kill All Sessions + Screens</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>L</kbd></div><div>Clear Terminal</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>+</kbd></div><div>Increase Font</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>-</kbd></div><div>Decrease Font</div>
|
||
<div><kbd>Ctrl</kbd>+<kbd>?</kbd></div><div>Show Help</div>
|
||
<div><kbd>Escape</kbd></div><div>Close Panels</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Monitor Panel (combined Process Monitor + Background Tasks) -->
|
||
<div class="monitor-panel" id="monitorPanel">
|
||
<div class="monitor-panel-header" id="monitorPanelHeader">
|
||
<span>Monitor</span>
|
||
<div class="monitor-panel-actions">
|
||
<button class="btn-toolbar btn-sm btn-danger" onclick="app.killAllSessions()" title="Kill all sessions and their screen processes">Kill All</button>
|
||
<button class="btn-icon-sm" onclick="app.reconcileScreens()" title="Refresh screens">↻</button>
|
||
<button class="btn-icon-sm" onclick="app.toggleMonitorDetach()" title="Detach panel" id="monitorDetachBtn">⧉</button>
|
||
<button class="btn-icon-sm" onclick="app.toggleMonitorPanel()" title="Toggle panel" id="monitorToggleBtn">▲</button>
|
||
<button class="btn-icon-sm" onclick="app.closeMonitor()" title="Close monitor">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="monitor-resize-handle" id="monitorResizeHandle"></div>
|
||
<div class="monitor-panel-body">
|
||
<div class="monitor-section">
|
||
<div class="monitor-section-header">
|
||
<span>Screen Sessions</span>
|
||
</div>
|
||
<div class="monitor-section-body" id="screenSessionsBody">
|
||
<div class="monitor-empty">No screen sessions</div>
|
||
</div>
|
||
</div>
|
||
<div class="monitor-section" id="backgroundTasksSection" style="display: none;">
|
||
<div class="monitor-section-header">
|
||
<span>Background Tasks</span>
|
||
<span class="task-stats" id="taskPanelStats">0 running</span>
|
||
</div>
|
||
<div class="monitor-section-body" id="backgroundTasksBody">
|
||
</div>
|
||
</div>
|
||
<div class="monitor-section" id="subagentSection">
|
||
<div class="monitor-section-header">
|
||
<span>Subagents</span>
|
||
<span class="subagent-badge" id="subagentBadge" style="display: none;"></span>
|
||
</div>
|
||
<div class="monitor-section-body">
|
||
<div class="subagent-container">
|
||
<div class="subagent-list" id="subagentList">
|
||
<div class="subagent-empty">No background agents detected</div>
|
||
</div>
|
||
<div class="subagent-detail" id="subagentDetail">
|
||
<div class="subagent-empty">Select an agent to view details</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Session Options Modal (includes Respawn Settings) -->
|
||
<div class="modal" id="sessionOptionsModal">
|
||
<div class="modal-backdrop" onclick="app.closeSessionOptions()"></div>
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Session Options</h3>
|
||
<button class="modal-close" onclick="app.closeSessionOptions()">×</button>
|
||
</div>
|
||
<!-- Modal Tabs -->
|
||
<div class="modal-tabs">
|
||
<button class="modal-tab-btn active" data-tab="respawn" onclick="app.switchOptionsTab('respawn')">Respawn</button>
|
||
<button class="modal-tab-btn" data-tab="context" onclick="app.switchOptionsTab('context')">Context</button>
|
||
<button class="modal-tab-btn" data-tab="ralph" onclick="app.switchOptionsTab('ralph')">Ralph / Todo</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<!-- Respawn Tab Content -->
|
||
<div class="modal-tab-content" id="respawn-tab">
|
||
<!-- Respawn Settings Section -->
|
||
<div class="session-respawn-section" id="sessionRespawnSection">
|
||
<div class="section-divider">
|
||
<div class="session-respawn-status" id="sessionRespawnStatus">
|
||
<span class="respawn-status-indicator"></span>
|
||
<span class="respawn-status-text">Not active</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Run Duration</label>
|
||
<div class="duration-presets">
|
||
<button type="button" class="duration-preset-btn" data-minutes="30" onclick="app.selectDurationPreset('30')">30m</button>
|
||
<button type="button" class="duration-preset-btn" data-minutes="60" onclick="app.selectDurationPreset('60')">1h</button>
|
||
<button type="button" class="duration-preset-btn" data-minutes="120" onclick="app.selectDurationPreset('120')">2h</button>
|
||
<button type="button" class="duration-preset-btn" data-minutes="240" onclick="app.selectDurationPreset('240')">4h</button>
|
||
<button type="button" class="duration-preset-btn" data-minutes="480" onclick="app.selectDurationPreset('480')">8h</button>
|
||
<button type="button" class="duration-preset-btn active" data-minutes="" onclick="app.selectDurationPreset('')">∞</button>
|
||
<div class="duration-custom">
|
||
<button type="button" class="duration-preset-btn" data-minutes="custom" onclick="app.selectDurationPreset('custom')">Custom</button>
|
||
<div class="input-suffix input-suffix-sm duration-custom-input">
|
||
<input type="number" id="modalRespawnDuration" value="" min="1" placeholder="min">
|
||
<span>min</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<span class="form-hint">How long to run the respawn loop</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Update Prompt</label>
|
||
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle" onchange="app.autoSaveRespawnConfig()">
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>After Update Prompt</label>
|
||
<div class="checkbox-group-inline">
|
||
<label class="checkbox-inline">
|
||
<input type="checkbox" id="modalRespawnSendClear" checked onchange="app.autoSaveRespawnConfig()">
|
||
<span>Send /clear</span>
|
||
</label>
|
||
<label class="checkbox-inline">
|
||
<input type="checkbox" id="modalRespawnSendInit" checked onchange="app.autoSaveRespawnConfig()">
|
||
<span>Send /init</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Kickstart Prompt</label>
|
||
<input type="text" id="modalRespawnKickstart" value="" placeholder="Optional: prompt if /init doesn't trigger work" onchange="app.autoSaveRespawnConfig()">
|
||
<span class="form-hint">Sent only when /init completes but Claude stays idle</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Auto-Accept Plan Mode</label>
|
||
<div class="checkbox-group-inline">
|
||
<label class="checkbox-inline">
|
||
<input type="checkbox" id="modalRespawnAutoAccept" checked onchange="app.autoSaveRespawnConfig()">
|
||
<span>Accept plan mode approvals</span>
|
||
</label>
|
||
</div>
|
||
<span class="form-hint">Presses Enter to accept plan approvals and default question options when Claude is waiting</span>
|
||
</div>
|
||
|
||
<div class="respawn-actions">
|
||
<button class="btn-toolbar btn-success" onclick="app.enableRespawnFromModal()" id="modalEnableRespawnBtn">Enable Respawn</button>
|
||
<button class="btn-toolbar btn-danger" onclick="app.stopRespawnFromModal()" id="modalStopRespawnBtn" style="display: none;">Stop Respawn</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- End respawn-tab -->
|
||
|
||
<!-- Context Management Tab Content -->
|
||
<div class="modal-tab-content hidden" id="context-tab">
|
||
<div class="form-row">
|
||
<label>Auto-Compact Context</label>
|
||
<div class="auto-clear-row">
|
||
<label class="checkbox-inline">
|
||
<input type="checkbox" id="modalAutoCompactEnabled" onchange="app.autoSaveAutoCompact()">
|
||
<span>Enable at</span>
|
||
</label>
|
||
<div class="input-suffix input-suffix-sm">
|
||
<input type="number" id="modalAutoCompactThreshold" value="110000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoCompact()">
|
||
<span>tokens</span>
|
||
</div>
|
||
</div>
|
||
<input type="text" id="modalAutoCompactPrompt" value="" placeholder="Optional: focus prompt for compact" class="mt-sm" onchange="app.autoSaveAutoCompact()">
|
||
<span class="form-hint">Summarizes context while preserving key information</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Auto-Clear Context</label>
|
||
<div class="auto-clear-row">
|
||
<label class="checkbox-inline">
|
||
<input type="checkbox" id="modalAutoClearEnabled" onchange="app.autoSaveAutoClear()">
|
||
<span>Enable at</span>
|
||
</label>
|
||
<div class="input-suffix input-suffix-sm">
|
||
<input type="number" id="modalAutoClearThreshold" value="140000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoClear()">
|
||
<span>tokens</span>
|
||
</div>
|
||
</div>
|
||
<span class="form-hint">Completely resets context (use higher threshold than compact)</span>
|
||
</div>
|
||
</div><!-- End context-tab -->
|
||
|
||
<!-- Ralph Wiggum Tab Content -->
|
||
<div class="modal-tab-content hidden" id="ralph-tab">
|
||
<div class="form-row form-row-switch">
|
||
<label>Enable Tracker</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="modalRalphEnabled">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Enable Ralph / Todo tracker for this session (auto-detects loops and todos)</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Completion Phrase</label>
|
||
<input type="text" id="modalRalphPhrase" placeholder="e.g., COMPLETE, DONE, FINISHED">
|
||
<span class="form-hint">The phrase Claude outputs when the loop is complete (without <promise> tags)</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Max Iterations</label>
|
||
<input type="number" id="modalRalphMaxIterations" value="0" min="0" max="1000">
|
||
<span class="form-hint">Maximum respawn cycles (0 = unlimited)</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Max Todos</label>
|
||
<input type="number" id="modalRalphMaxTodos" value="50" min="1" max="500">
|
||
<span class="form-hint">Maximum todos to track per session</span>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Todo Expiration</label>
|
||
<div class="input-suffix input-suffix-sm">
|
||
<input type="number" id="modalRalphTodoExpiration" value="60" min="1" max="1440">
|
||
<span>min</span>
|
||
</div>
|
||
<span class="form-hint">Minutes before todos auto-expire</span>
|
||
</div>
|
||
|
||
<div class="ralph-config-actions">
|
||
<button class="btn-toolbar btn-primary" onclick="app.saveRalphConfig()">Save Ralph Config</button>
|
||
</div>
|
||
</div><!-- End ralph-tab -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Close Session Confirmation Modal -->
|
||
<div class="modal" id="closeConfirmModal">
|
||
<div class="modal-backdrop" onclick="app.cancelCloseSession()"></div>
|
||
<div class="modal-content modal-sm">
|
||
<div class="modal-header">
|
||
<h3>Close Session</h3>
|
||
<button class="modal-close" onclick="app.cancelCloseSession()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="modal-session-name" id="closeConfirmSessionName"></p>
|
||
</div>
|
||
<div class="close-options">
|
||
<button class="close-option" onclick="app.confirmCloseSession(false)">
|
||
<span class="close-option-title">Remove Tab</span>
|
||
<span class="close-option-desc">Screen session keeps running in background</span>
|
||
</button>
|
||
<button class="close-option close-option-danger" onclick="app.confirmCloseSession(true)">
|
||
<span class="close-option-title">Kill Claude</span>
|
||
<span class="close-option-desc">Terminate the session completely</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-footer-cancel">
|
||
<button class="btn-toolbar btn-sm" onclick="app.cancelCloseSession()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- App Settings Modal -->
|
||
<div class="modal" id="appSettingsModal">
|
||
<div class="modal-backdrop" onclick="app.closeAppSettings()"></div>
|
||
<div class="modal-content modal-lg">
|
||
<div class="modal-header">
|
||
<h3>App Settings</h3>
|
||
<button class="modal-close" onclick="app.closeAppSettings()">×</button>
|
||
</div>
|
||
<div class="modal-tabs">
|
||
<button class="modal-tab-btn active" data-tab="settings-display">Display</button>
|
||
<button class="modal-tab-btn" data-tab="settings-claude">Claude CLI</button>
|
||
<button class="modal-tab-btn" data-tab="settings-paths">Paths</button>
|
||
<button class="modal-tab-btn" data-tab="settings-notifications">Notifications</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<!-- Display Tab -->
|
||
<div class="modal-tab-content" id="settings-display">
|
||
<div class="form-row form-row-switch">
|
||
<label>Show Font Controls</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsShowFontControls" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Show A-/A+ font size buttons in header</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Show System Stats</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsShowSystemStats" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Show CPU and memory usage in header</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Show Token Count</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsShowTokenCount" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Show total token count in header</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Show Monitor Panel</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsShowMonitor" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Show Monitor panel at bottom right</span>
|
||
</div>
|
||
</div>
|
||
<!-- Claude CLI Tab -->
|
||
<div class="modal-tab-content hidden" id="settings-claude">
|
||
<div class="form-row">
|
||
<label>Startup Mode</label>
|
||
<select id="appSettingsClaudeMode" class="form-select">
|
||
<option value="dangerously-skip-permissions">Skip Permissions (default)</option>
|
||
<option value="normal">Normal (with prompts)</option>
|
||
<option value="allowedTools">Allowed Tools Only</option>
|
||
</select>
|
||
<span class="form-hint">How Claude CLI is started in screen sessions</span>
|
||
</div>
|
||
<div class="form-row" id="allowedToolsRow" style="display: none;">
|
||
<label>Allowed Tools</label>
|
||
<input type="text" id="appSettingsAllowedTools" placeholder="Read,Glob,Grep,Bash">
|
||
<span class="form-hint">Comma-separated list of tools to allow</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Enable Ralph / Todo Tracker</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsRalphEnabled">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Auto-enable for new sessions (otherwise auto-enables on Ralph pattern detection)</span>
|
||
</div>
|
||
</div>
|
||
<!-- Paths Tab -->
|
||
<div class="modal-tab-content hidden" id="settings-paths">
|
||
<div class="form-row">
|
||
<label>Default CLAUDE.md Template</label>
|
||
<input type="text" id="appSettingsClaudeMdPath" placeholder="/path/to/CLAUDE.md template">
|
||
<span class="form-hint">Used when creating new cases. Leave empty for built-in template.</span>
|
||
</div>
|
||
<div class="form-row">
|
||
<label>Default Working Directory</label>
|
||
<input type="text" id="appSettingsDefaultDir" placeholder="~/projects">
|
||
<span class="form-hint">Default directory for new sessions.</span>
|
||
</div>
|
||
</div>
|
||
<!-- Notifications Tab -->
|
||
<div class="modal-tab-content hidden" id="settings-notifications">
|
||
<div class="form-row form-row-switch">
|
||
<label>Enable Notifications</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifEnabled" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Master toggle for all notification layers</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Browser Notifications</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifBrowser">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Show OS-level notifications when tab is hidden</span>
|
||
</div>
|
||
<div class="form-row">
|
||
<button class="btn-toolbar btn-sm" onclick="app.notificationManager?.requestPermission()">Request Permission</button>
|
||
<span class="form-hint" id="notifPermissionStatus">Status: unknown</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Audio Alerts</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifAudio">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Play a short beep for critical events</span>
|
||
</div>
|
||
<div class="form-row">
|
||
<label>Idle Threshold (minutes)</label>
|
||
<input type="number" id="appSettingsNotifStuckMins" value="10" min="1" max="120" style="width: 80px;">
|
||
<span class="form-hint">Notify when a session is idle longer than this</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Critical Notifications</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifCritical" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Errors, crashes, agent failures</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Warning Notifications</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifWarning" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Completions, budget warnings, stuck sessions</span>
|
||
</div>
|
||
<div class="form-row form-row-switch">
|
||
<label>Info Notifications</label>
|
||
<label class="switch">
|
||
<input type="checkbox" id="appSettingsNotifInfo" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="form-hint">Auto-accepts, auto-clears, agent completions</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn-toolbar" onclick="app.closeAppSettings()">Cancel</button>
|
||
<button class="btn-toolbar btn-primary" onclick="app.saveAppSettings()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create Case Modal -->
|
||
<div class="modal" id="createCaseModal">
|
||
<div class="modal-backdrop" onclick="app.closeCreateCaseModal()"></div>
|
||
<div class="modal-content modal-lg">
|
||
<div class="modal-header">
|
||
<h3>Add Case</h3>
|
||
<button class="modal-close" onclick="app.closeCreateCaseModal()">×</button>
|
||
</div>
|
||
<div class="modal-tabs">
|
||
<button class="modal-tab-btn active" data-tab="case-create">Create New</button>
|
||
<button class="modal-tab-btn" data-tab="case-link">Link Existing</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<!-- Create New Tab -->
|
||
<div class="modal-tab-content" id="case-create">
|
||
<div class="form-row">
|
||
<label>Case Name</label>
|
||
<input type="text" id="newCaseName" placeholder="my-project" pattern="[a-zA-Z0-9_-]+">
|
||
<span class="form-hint">Letters, numbers, hyphens, underscores only. Created in ~/claudeman-cases/</span>
|
||
</div>
|
||
<div class="form-row">
|
||
<label>Description (optional)</label>
|
||
<input type="text" id="newCaseDescription" placeholder="A brief description...">
|
||
</div>
|
||
</div>
|
||
<!-- Link Existing Tab -->
|
||
<div class="modal-tab-content hidden" id="case-link">
|
||
<div class="form-row">
|
||
<label>Case Name</label>
|
||
<input type="text" id="linkCaseName" placeholder="my-project" pattern="[a-zA-Z0-9_-]+">
|
||
<span class="form-hint">Name to identify this case in Claudeman</span>
|
||
</div>
|
||
<div class="form-row">
|
||
<label>Folder Path</label>
|
||
<input type="text" id="linkCasePath" placeholder="/home/user/projects/my-project">
|
||
<span class="form-hint">Absolute path to an existing project folder</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn-toolbar" onclick="app.closeCreateCaseModal()">Cancel</button>
|
||
<button class="btn-toolbar btn-primary" id="caseModalSubmit" onclick="app.submitCaseModal()">Create</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Kill All Confirmation Modal -->
|
||
<div class="modal" id="killAllModal">
|
||
<div class="modal-backdrop" onclick="app.closeKillAllModal()"></div>
|
||
<div class="modal-content modal-sm">
|
||
<div class="modal-header">
|
||
<h3>Kill All Sessions</h3>
|
||
<button class="modal-close" onclick="app.closeKillAllModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="modal-warning-text">Choose how to close <span id="killAllCount">0</span> session(s):</p>
|
||
</div>
|
||
<div class="close-options">
|
||
<button class="close-option" onclick="app.confirmKillAll(false)">
|
||
<span class="close-option-title">Remove All Tabs</span>
|
||
<span class="close-option-desc">Screen sessions keep running in background</span>
|
||
</button>
|
||
<button class="close-option close-option-danger" onclick="app.confirmKillAll(true)">
|
||
<span class="close-option-title">Kill All Claude + Screens</span>
|
||
<span class="close-option-desc">Terminate everything completely</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-footer-cancel">
|
||
<button class="btn-toolbar btn-sm" onclick="app.closeKillAllModal()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Notification Drawer -->
|
||
<div class="notification-drawer" id="notifDrawer">
|
||
<div class="notif-drawer-header">
|
||
<span class="notif-drawer-title">Notifications</span>
|
||
<div class="notif-drawer-actions">
|
||
<button class="btn-notif-action" onclick="app.notificationManager?.markAllRead()" title="Mark all read">✓</button>
|
||
<button class="btn-notif-action" onclick="app.notificationManager?.clearAll()" title="Clear all">🗑</button>
|
||
<button class="btn-notif-action" onclick="app.toggleNotifications()" title="Close">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="notif-drawer-list" id="notifList"></div>
|
||
<div class="notif-drawer-empty" id="notifEmpty">No notifications</div>
|
||
</div>
|
||
|
||
<!-- Help Button -->
|
||
<button class="help-btn" onclick="app.showHelp()" title="Keyboard shortcuts (Ctrl+?)">?</button>
|
||
</div>
|
||
|
||
<script src="app.js"></script>
|
||
</body>
|
||
</html>
|