mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
feat: improve UI - larger monitor, cleaner close modal, remove toolbar buttons
- Monitor panel now 500px wide and 80vh max height - Removed Copy, Clear, Monitor, Kill buttons from toolbar - Redesigned close session modal with cleaner options: - "Remove Tab" keeps screen running - "Kill Claude" terminates completely - Auto-switch to new session when created Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -344,102 +344,24 @@ npx vitest run -t "should create session" # By pattern
|
|||||||
|
|
||||||
### E2E Testing with agent-browser
|
### E2E Testing with agent-browser
|
||||||
|
|
||||||
For UI testing, we use [agent-browser](https://github.com/vercel-labs/agent-browser) - a fast CLI for browser automation optimized for AI agents.
|
For UI testing, use [agent-browser](https://github.com/vercel-labs/agent-browser). A full E2E test plan is documented in `.claude/skills/e2e-test.md`.
|
||||||
|
|
||||||
**Installation:**
|
|
||||||
```bash
|
```bash
|
||||||
npm install agent-browser --save-dev
|
# Setup
|
||||||
npx agent-browser install # Download Chromium
|
npx agent-browser install # Download Chromium (one-time)
|
||||||
```
|
npx tsx src/index.ts web & # Start server
|
||||||
|
|
||||||
**Basic E2E Test Flow:**
|
# Basic test flow
|
||||||
```bash
|
|
||||||
# Start the server
|
|
||||||
npx tsx src/index.ts web &
|
|
||||||
|
|
||||||
# Open browser
|
|
||||||
npx agent-browser open http://localhost:3000
|
npx agent-browser open http://localhost:3000
|
||||||
|
npx agent-browser snapshot # Get accessibility tree with element refs
|
||||||
# Get page snapshot (shows accessibility tree with element refs)
|
npx agent-browser click @e5 # Click by element ref
|
||||||
npx agent-browser snapshot
|
npx agent-browser find text "Run Claude" click # Or use semantic locators
|
||||||
|
npx agent-browser eval "app.sessions.get(app.activeSessionId)" # Run JS
|
||||||
# Click elements by reference
|
npx agent-browser screenshot /tmp/test.png
|
||||||
npx agent-browser click @e5 # Click element with ref=e5
|
|
||||||
|
|
||||||
# Or use semantic locators
|
|
||||||
npx agent-browser find text "Run Claude" click
|
|
||||||
npx agent-browser find role button click --name "Monitor"
|
|
||||||
|
|
||||||
# Fill inputs
|
|
||||||
npx agent-browser fill @e32 "my-session-name"
|
|
||||||
|
|
||||||
# Execute JavaScript in page context
|
|
||||||
npx agent-browser eval "app.sessions.get(app.activeSessionId)"
|
|
||||||
|
|
||||||
# Take screenshots
|
|
||||||
npx agent-browser screenshot /tmp/test-result.png
|
|
||||||
|
|
||||||
# Close browser
|
|
||||||
npx agent-browser close
|
npx agent-browser close
|
||||||
```
|
```
|
||||||
|
|
||||||
**E2E Test Skill (`.claude/skills/e2e-test.md`):**
|
**Key test areas:** Initial load, font controls (A-/A+), tab count stepper, session creation with screen wrapping, session options modal, monitor panel.
|
||||||
|
|
||||||
A skill file exists that documents the full E2E test plan. Key tests include:
|
|
||||||
|
|
||||||
1. **Initial Load**: Verify header layout (font controls, connection status, tokens)
|
|
||||||
2. **Font Controls**: Test A-/A+ buttons change font size
|
|
||||||
3. **Tab Count Stepper**: Test −/+ buttons increment/decrement
|
|
||||||
4. **Session Creation**: Create session, verify screen wrapping
|
|
||||||
5. **Session Options Modal**: Open gear icon, verify respawn settings visible
|
|
||||||
6. **Monitor Panel**: Test Screen Sessions and Background Tasks display
|
|
||||||
|
|
||||||
**Example Test Session:**
|
|
||||||
```bash
|
|
||||||
# Clean up previous screens
|
|
||||||
screen -ls | grep -oP '\d+\.claudeman-[a-z0-9]+' | while read s; do
|
|
||||||
screen -S "$s" -X quit
|
|
||||||
done
|
|
||||||
|
|
||||||
# Start fresh server
|
|
||||||
rm -f ~/.claudeman/screens.json
|
|
||||||
npx tsx src/index.ts web &
|
|
||||||
sleep 4
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
npx agent-browser open http://localhost:3000
|
|
||||||
npx agent-browser snapshot | head -30 # Check initial state
|
|
||||||
npx agent-browser click @e5 # Run Claude
|
|
||||||
sleep 4
|
|
||||||
npx agent-browser snapshot # Verify session created
|
|
||||||
|
|
||||||
# Check screen wrapping
|
|
||||||
screen -ls | grep claudeman # Should show screen session
|
|
||||||
|
|
||||||
# Check session has PID
|
|
||||||
npx agent-browser eval "app.sessions.get(app.activeSessionId).pid"
|
|
||||||
|
|
||||||
# Test session options
|
|
||||||
npx agent-browser eval "document.querySelector('[title=\"Session options\"]').click()"
|
|
||||||
npx agent-browser snapshot | grep -E "Respawn|Enable" # Should show respawn settings
|
|
||||||
|
|
||||||
npx agent-browser screenshot /tmp/test-final.png
|
|
||||||
npx agent-browser close
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key Commands:**
|
|
||||||
| Command | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| `open <url>` | Navigate to URL |
|
|
||||||
| `snapshot` | Get accessibility tree with element refs |
|
|
||||||
| `snapshot -i` | Interactive elements only |
|
|
||||||
| `click @ref` | Click element by ref |
|
|
||||||
| `fill @ref "text"` | Fill input field |
|
|
||||||
| `eval "js code"` | Execute JavaScript |
|
|
||||||
| `screenshot path` | Save screenshot |
|
|
||||||
| `find text/role/label "x" click` | Semantic element location |
|
|
||||||
| `wait 1000` | Wait milliseconds |
|
|
||||||
| `close` | Close browser |
|
|
||||||
|
|
||||||
## Frontend
|
## Frontend
|
||||||
|
|
||||||
@@ -449,8 +371,8 @@ The web UI (`src/web/public/`) uses vanilla JavaScript with:
|
|||||||
- **Server-Sent Events**: Real-time updates from `/api/events`
|
- **Server-Sent Events**: Real-time updates from `/api/events`
|
||||||
- **No build step**: Static files served directly by Fastify
|
- **No build step**: Static files served directly by Fastify
|
||||||
|
|
||||||
## Pending Tasks
|
## Notes
|
||||||
|
|
||||||
**Note to Claude: Do NOT remove or modify this section during /init. These tasks may be actively worked on by other sessions.**
|
- State persists to `~/.claudeman/state.json` and `~/.claudeman/screens.json`
|
||||||
|
- Cases are created in `~/claudeman-cases/` by default
|
||||||
<!-- Add pending tasks here -->
|
- Sessions are wrapped in GNU screen for persistence across server restarts
|
||||||
|
|||||||
+2
-12
@@ -763,20 +763,10 @@ class ClaudemanApp {
|
|||||||
this.terminal.writeln(`\x1b[90m Created session ${i}/${tabCount}: ${sessionName}\x1b[0m`);
|
this.terminal.writeln(`\x1b[90m Created session ${i}/${tabCount}: ${sessionName}\x1b[0m`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the first session
|
// Auto-switch to the new session
|
||||||
if (firstSessionId) {
|
if (firstSessionId) {
|
||||||
this.activeSessionId = firstSessionId;
|
await this.selectSession(firstSessionId);
|
||||||
this.loadQuickStartCases();
|
this.loadQuickStartCases();
|
||||||
|
|
||||||
// Send resize
|
|
||||||
const dims = this.fitAddon.proposeDimensions();
|
|
||||||
if (dims) {
|
|
||||||
await fetch(`/api/sessions/${firstSessionId}/resize`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.terminal.focus();
|
this.terminal.focus();
|
||||||
|
|||||||
+14
-13
@@ -96,13 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<button class="btn-toolbar" onclick="app.copyTerminal()" title="Copy terminal">Copy</button>
|
<!-- Buttons removed - use keyboard shortcuts instead -->
|
||||||
<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>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
@@ -264,17 +258,24 @@
|
|||||||
<div class="modal-backdrop" onclick="app.cancelCloseSession()"></div>
|
<div class="modal-backdrop" onclick="app.cancelCloseSession()"></div>
|
||||||
<div class="modal-content modal-sm">
|
<div class="modal-content modal-sm">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>Close Session?</h3>
|
<h3>Close Session</h3>
|
||||||
<button class="modal-close" onclick="app.cancelCloseSession()">×</button>
|
<button class="modal-close" onclick="app.cancelCloseSession()">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="modal-session-name" id="closeConfirmSessionName"></p>
|
<p class="modal-session-name" id="closeConfirmSessionName"></p>
|
||||||
<p class="modal-warning-text">Choose how to close this session:</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions form-actions-stack">
|
<div class="close-options">
|
||||||
<button class="btn-toolbar btn-warning" onclick="app.confirmCloseSession(false)" title="Remove tab but keep screen session running">Hide Tab (Keep Screen)</button>
|
<button class="close-option" onclick="app.confirmCloseSession(false)">
|
||||||
<button class="btn-toolbar btn-danger" onclick="app.confirmCloseSession(true)" title="Terminate the screen session completely">Close & Kill Screen</button>
|
<span class="close-option-title">Remove Tab</span>
|
||||||
<button class="btn-toolbar" onclick="app.cancelCloseSession()">Cancel</button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-content.modal-sm {
|
.modal-content.modal-sm {
|
||||||
max-width: 320px;
|
max-width: 340px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-warning-text {
|
.modal-warning-text {
|
||||||
@@ -859,6 +859,65 @@ body {
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Close Session Options */
|
||||||
|
.close-options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--bg-hover);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option:hover {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option-danger {
|
||||||
|
border-color: var(--red);
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option-danger:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.2);
|
||||||
|
border-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option-title {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option-danger .close-option-title {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-option-desc {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer-cancel {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
.modal-session-name {
|
.modal-session-name {
|
||||||
font-family: 'SF Mono', Monaco, monospace;
|
font-family: 'SF Mono', Monaco, monospace;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@@ -1420,9 +1479,9 @@ kbd {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: var(--toolbar-height);
|
bottom: var(--toolbar-height);
|
||||||
right: 0.5rem;
|
right: 0.5rem;
|
||||||
width: 420px;
|
width: 500px;
|
||||||
max-width: calc(100vw - 1rem);
|
max-width: calc(100vw - 1rem);
|
||||||
max-height: 60vh;
|
max-height: 80vh;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user