feat(ui): add shell instance count and improve welcome message

- Add instance count controls (+/- buttons) for Run Shell
- Update runShell() to support launching multiple shell sessions
- Update welcome message to explain persistent GNU Screen sessions
- Add "Instance count" tooltips to both Claude and Shell controls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-21 13:16:13 +01:00
co-authored by Claude Opus 4.5
parent f61df0b89e
commit 951d13b6e6
2 changed files with 57 additions and 29 deletions
+48 -25
View File
@@ -184,7 +184,11 @@ class ClaudemanApp {
showWelcome() {
this.terminal.writeln('\x1b[1;36m Claudeman Terminal\x1b[0m');
this.terminal.writeln('\x1b[90m Click "Run Claude" or press Ctrl+Enter to begin\x1b[0m');
this.terminal.writeln('');
this.terminal.writeln('\x1b[90m Each instance opens a persistent GNU Screen session running Claude or Shell.\x1b[0m');
this.terminal.writeln('\x1b[90m Sessions stay alive for autonomous work, even if you close this browser.\x1b[0m');
this.terminal.writeln('');
this.terminal.writeln('\x1b[90m Press \x1b[1;37mCtrl+Enter\x1b[0m\x1b[90m to start Claude • Click \x1b[1;37mRun Shell\x1b[0m\x1b[90m for a terminal\x1b[0m');
this.terminal.writeln('');
}
@@ -990,6 +994,19 @@ class ClaudemanApp {
input.value = Math.max(1, current - 1);
}
// Shell count stepper functions
incrementShellCount() {
const input = document.getElementById('shellCount');
const current = parseInt(input.value) || 1;
input.value = Math.min(20, current + 1);
}
decrementShellCount() {
const input = document.getElementById('shellCount');
const current = parseInt(input.value) || 1;
input.value = Math.max(1, current - 1);
}
async runClaude() {
const caseName = document.getElementById('quickStartCase').value || 'testcase';
const tabCount = Math.min(20, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
@@ -1084,9 +1101,10 @@ class ClaudemanApp {
async runShell() {
const caseName = document.getElementById('quickStartCase').value || 'testcase';
const shellCount = Math.min(20, Math.max(1, parseInt(document.getElementById('shellCount').value) || 1));
this.terminal.clear();
this.terminal.writeln(`\x1b[1;33m Starting Shell in ${caseName}...\x1b[0m`);
this.terminal.writeln(`\x1b[1;33m Starting ${shellCount} Shell session(s) in ${caseName}...\x1b[0m`);
this.terminal.writeln('');
try {
@@ -1107,34 +1125,39 @@ class ClaudemanApp {
}
}
}
const sessionName = `s${startNumber}-${caseName}`;
// Create session with shell mode
const createRes = await fetch('/api/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workingDir, mode: 'shell', name: sessionName })
});
const createData = await createRes.json();
if (!createData.success) throw new Error(createData.error);
// Create multiple shell sessions
for (let i = 0; i < shellCount; i++) {
const sessionName = `s${startNumber + i}-${caseName}`;
const sessionId = createData.session.id;
// Start shell
await fetch(`/api/sessions/${sessionId}/shell`, {
method: 'POST'
});
this.activeSessionId = sessionId;
// Send resize
const dims = this.fitAddon.proposeDimensions();
if (dims) {
await fetch(`/api/sessions/${sessionId}/resize`, {
// Create session with shell mode
const createRes = await fetch('/api/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
body: JSON.stringify({ workingDir, mode: 'shell', name: sessionName })
});
const createData = await createRes.json();
if (!createData.success) throw new Error(createData.error);
const sessionId = createData.session.id;
// Start shell
await fetch(`/api/sessions/${sessionId}/shell`, {
method: 'POST'
});
// Set active to last created
this.activeSessionId = sessionId;
// Send resize
const dims = this.fitAddon.proposeDimensions();
if (dims) {
await fetch(`/api/sessions/${sessionId}/resize`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
});
}
}
this.terminal.focus();
+9 -4
View File
@@ -165,14 +165,19 @@
<button class="btn-toolbar btn-claude" 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="20" readonly>
<button class="tab-count-btn" onclick="app.incrementTabCount()">+</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>