mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 16:39:42 +02:00
feat: add multi-tab feature to open 1-10 Claude sessions at once
- Add number input (1-10) next to "Run Claude" button in toolbar - When tabCount > 1, create sessions named "1-projectname", "2-projectname", etc. - All sessions share the same working directory (case folder) - First session is auto-selected after creation - Add CSS for small number input in toolbar Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+56
-17
@@ -678,32 +678,71 @@ class ClaudemanApp {
|
|||||||
|
|
||||||
async runClaude() {
|
async runClaude() {
|
||||||
const caseName = document.getElementById('quickStartCase').value || 'testcase';
|
const caseName = document.getElementById('quickStartCase').value || 'testcase';
|
||||||
|
const tabCount = Math.min(10, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
|
||||||
|
|
||||||
this.terminal.clear();
|
this.terminal.clear();
|
||||||
this.terminal.writeln(`\x1b[1;32m Starting Claude in ${caseName}...\x1b[0m`);
|
this.terminal.writeln(`\x1b[1;32m Starting ${tabCount} Claude session(s) in ${caseName}...\x1b[0m`);
|
||||||
this.terminal.writeln('');
|
this.terminal.writeln('');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/quick-start', {
|
// Get case path first
|
||||||
method: 'POST',
|
const caseRes = await fetch(`/api/cases/${caseName}`);
|
||||||
headers: { 'Content-Type': 'application/json' },
|
const caseData = await caseRes.json();
|
||||||
body: JSON.stringify({ caseName })
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
if (!data.success) throw new Error(data.error);
|
// Create the case if it doesn't exist
|
||||||
|
if (!caseData.path) {
|
||||||
this.activeSessionId = data.sessionId;
|
const createCaseRes = await fetch('/api/cases', {
|
||||||
this.loadQuickStartCases();
|
|
||||||
|
|
||||||
// Send resize
|
|
||||||
const dims = this.fitAddon.proposeDimensions();
|
|
||||||
if (dims) {
|
|
||||||
await fetch(`/api/sessions/${data.sessionId}/resize`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
body: JSON.stringify({ name: caseName, description: '' })
|
||||||
});
|
});
|
||||||
|
const createCaseData = await createCaseRes.json();
|
||||||
|
if (!createCaseData.success) throw new Error(createCaseData.error || 'Failed to create case');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workingDir = caseData.path || `${process.env.HOME}/claudeman-cases/${caseName}`;
|
||||||
|
let firstSessionId = null;
|
||||||
|
|
||||||
|
// Create multiple sessions
|
||||||
|
for (let i = 1; i <= tabCount; i++) {
|
||||||
|
const sessionName = tabCount > 1 ? `${i}-${caseName}` : caseName;
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
const createRes = await fetch('/api/sessions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ workingDir, name: sessionName })
|
||||||
|
});
|
||||||
|
const createData = await createRes.json();
|
||||||
|
if (!createData.success) throw new Error(createData.error);
|
||||||
|
|
||||||
|
// Start interactive mode
|
||||||
|
await fetch(`/api/sessions/${createData.session.id}/interactive`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Track first session
|
||||||
|
if (i === 1) {
|
||||||
|
firstSessionId = createData.session.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.terminal.writeln(`\x1b[90m Created session ${i}/${tabCount}: ${sessionName}\x1b[0m`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the first session
|
||||||
|
if (firstSessionId) {
|
||||||
|
this.activeSessionId = firstSessionId;
|
||||||
|
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();
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
<button class="btn-toolbar btn-primary" onclick="app.runClaude()" title="Run Claude (Ctrl+Enter)">
|
<button class="btn-toolbar btn-primary" onclick="app.runClaude()" title="Run Claude (Ctrl+Enter)">
|
||||||
Run Claude
|
Run Claude
|
||||||
</button>
|
</button>
|
||||||
|
<input type="number" id="tabCount" class="toolbar-input-sm" value="1" min="1" max="10" title="Number of tabs to open (1-10)">
|
||||||
<button class="btn-toolbar" onclick="app.runShell()" title="Run Shell">
|
<button class="btn-toolbar" onclick="app.runShell()" title="Run Shell">
|
||||||
Shell
|
Shell
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -539,6 +539,28 @@ body {
|
|||||||
max-width: 50vw;
|
max-width: 50vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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 {
|
#dirDisplay {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
Reference in New Issue
Block a user