Files
Codeman/src/web/public/index.html
T
arkonandClaude Opus 4.5 bf41dab827 feat: UI improvements and E2E testing with agent-browser
- Move font controls (A+/A-) to left of connection status
- Redesign tab count with stepper buttons (−/number/+)
- Remove respawn panel, integrate settings into session options modal
- Fix Monitor panel SSE handler (wrong element ID)
- Add screen wrapping support to Session class
- Add E2E testing skill using agent-browser
- Document testing approach in README.md and CLAUDE.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 03:40:57 +01:00

316 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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="connection-status" id="connectionStatus">
<span class="status-dot"></span>
<span class="status-text">Connecting</span>
</div>
<div class="header-tokens" id="headerTokens" title="Total tokens across all sessions">0 tokens</div>
<button class="btn-icon-header" onclick="app.openAppSettings()" title="App Settings">&#x2699;</button>
</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">&#x25A0;</button>
</div>
<!-- Respawn Banner -->
<div class="respawn-banner" id="respawnBanner" style="display: none;">
<span class="respawn-indicator">&#x21BB;</span>
<span class="respawn-state" id="respawnState">Watching</span>
<span class="respawn-cycles">Cycle <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>
<span class="respawn-step" id="respawnStep"></span>
<button class="btn-icon-only" onclick="app.stopRespawn()" title="Stop Respawn">&#x25A0;</button>
</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-primary" onclick="app.runClaude()" title="Run Claude (Ctrl+Enter)">
Run Claude
</button>
<div class="tab-count-group" title="Number of tabs to open (1-10)">
<button class="tab-count-btn" onclick="app.decrementTabCount()">−</button>
<input type="number" id="tabCount" class="tab-count-input" value="1" min="1" max="10" readonly>
<button class="tab-count-btn" onclick="app.incrementTabCount()">+</button>
</div>
<button class="btn-toolbar" onclick="app.runShell()" title="Run Shell">
Shell
</button>
<select id="quickStartCase" class="toolbar-select" title="Select case">
<option value="testcase">testcase</option>
</select>
</div>
<!-- Directory -->
<div class="toolbar-group">
<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-right">
<button class="btn-toolbar" onclick="app.copyTerminal()" title="Copy terminal">Copy</button>
<button class="btn-toolbar" onclick="app.clearTerminal()" title="Clear terminal (Ctrl+L)">Clear</button>
<button class="btn-toolbar" onclick="app.toggleMonitorPanel()" title="Monitor">Monitor</button>
<div class="toolbar-divider"></div>
<button class="btn-toolbar btn-danger" onclick="app.killActiveSession()" title="Kill current session">Kill</button>
</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()">&times;</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</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">
<span>Monitor</span>
<div class="monitor-panel-actions">
<button class="btn-toolbar btn-sm" onclick="app.reconcileScreens()" title="Check for dead screens">Reconcile</button>
<button class="btn-close" onclick="app.toggleMonitorPanel()">&times;</button>
</div>
</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">
<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 class="monitor-empty">No background tasks</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()">&times;</button>
</div>
<div class="modal-body">
<div class="form-row">
<label>Session Name</label>
<input type="text" id="sessionNameInput" placeholder="Enter session name...">
</div>
<div class="form-row">
<label>Directory</label>
<div class="session-dir-display" id="sessionDirDisplay">/path/to/dir</div>
</div>
<!-- Respawn Settings Section -->
<div class="session-respawn-section" id="sessionRespawnSection">
<div class="section-divider">
<span>Respawn Controller</span>
<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>Update Prompt</label>
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle">
</div>
<div class="form-row-inline">
<div class="form-col">
<label>Idle Timeout</label>
<div class="input-suffix">
<input type="number" id="modalRespawnIdleTimeout" value="5" min="1" max="60">
<span>sec</span>
</div>
</div>
<div class="form-col">
<label>Step Delay</label>
<div class="input-suffix">
<input type="number" id="modalRespawnStepDelay" value="1" min="0" max="10">
<span>sec</span>
</div>
</div>
</div>
<div class="form-row">
<label>Run Duration</label>
<div class="input-suffix">
<input type="number" id="modalRespawnDuration" value="" min="1" placeholder="No limit">
<span>min</span>
</div>
<span class="form-hint">Leave empty for unlimited. Respawn stops after duration.</span>
</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>
<span>Send /clear</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnSendInit" checked>
<span>Send /init</span>
</label>
</div>
</div>
<div class="form-row">
<label>Auto-Clear Context</label>
<div class="auto-clear-row">
<label class="checkbox-inline">
<input type="checkbox" id="modalAutoClearEnabled">
<span>Enable at</span>
</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalAutoClearThreshold" value="100000" min="10000" max="500000" step="10000">
<span>tokens</span>
</div>
</div>
</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>
<div class="form-actions">
<button class="btn-toolbar" onclick="app.closeSessionOptions()">Cancel</button>
<button class="btn-toolbar btn-primary" onclick="app.saveSessionOptions()">Save Name</button>
</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()">&times;</button>
</div>
<div class="modal-body">
<p class="modal-warning-text">This will terminate the Claude session and any running processes.</p>
<p class="modal-session-name" id="closeConfirmSessionName"></p>
</div>
<div class="form-actions">
<button class="btn-toolbar" onclick="app.cancelCloseSession()">Cancel</button>
<button class="btn-toolbar btn-danger" onclick="app.confirmCloseSession()">Close Session</button>
</div>
</div>
</div>
<!-- App Settings Modal -->
<div class="modal" id="appSettingsModal">
<div class="modal-backdrop" onclick="app.closeAppSettings()"></div>
<div class="modal-content">
<div class="modal-header">
<h3>App Settings</h3>
<button class="modal-close" onclick="app.closeAppSettings()">&times;</button>
</div>
<div class="modal-body">
<div class="form-row">
<label>Default CLAUDE.md Template Path</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>
<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>
<!-- Help Button -->
<button class="help-btn" onclick="app.showHelp()" title="Keyboard shortcuts (Ctrl+?)">?</button>
</div>
<script src="app.js"></script>
</body>
</html>