mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 23:49:41 +02:00
feat(ui): add link existing folder as case and improve UX
- Add tabbed "Add Case" modal with "Create New" and "Link Existing" tabs - Add POST /api/cases/link endpoint to register existing folders - Store linked cases in ~/.claudeman/linked-cases.json - Update GET /api/cases to include linked cases - Add instance count controls (+/-) for Run Shell button - Update welcome message to explain instance count selector Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+88
-6
@@ -187,6 +187,7 @@ class ClaudemanApp {
|
||||
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('\x1b[90m Use the \x1b[1;37m+/-\x1b[0m\x1b[90m controls to set how many instances to launch at once.\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('');
|
||||
@@ -2635,25 +2636,65 @@ class ClaudemanApp {
|
||||
showCreateCaseModal() {
|
||||
document.getElementById('newCaseName').value = '';
|
||||
document.getElementById('newCaseDescription').value = '';
|
||||
document.getElementById('createCaseModal').classList.add('active');
|
||||
document.getElementById('linkCaseName').value = '';
|
||||
document.getElementById('linkCasePath').value = '';
|
||||
// Reset to first tab
|
||||
this.caseModalTab = 'case-create';
|
||||
this.switchCaseModalTab('case-create');
|
||||
// Wire up tab buttons
|
||||
const modal = document.getElementById('createCaseModal');
|
||||
modal.querySelectorAll('.modal-tabs .modal-tab-btn').forEach(btn => {
|
||||
btn.onclick = () => this.switchCaseModalTab(btn.dataset.tab);
|
||||
});
|
||||
modal.classList.add('active');
|
||||
document.getElementById('newCaseName').focus();
|
||||
}
|
||||
|
||||
switchCaseModalTab(tabName) {
|
||||
this.caseModalTab = tabName;
|
||||
const modal = document.getElementById('createCaseModal');
|
||||
// Toggle active class on tab buttons
|
||||
modal.querySelectorAll('.modal-tabs .modal-tab-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.tab === tabName);
|
||||
});
|
||||
// Toggle hidden class on tab content
|
||||
modal.querySelectorAll('.modal-tab-content').forEach(content => {
|
||||
content.classList.toggle('hidden', content.id !== tabName);
|
||||
});
|
||||
// Update submit button text
|
||||
const submitBtn = document.getElementById('caseModalSubmit');
|
||||
submitBtn.textContent = tabName === 'case-create' ? 'Create' : 'Link';
|
||||
// Focus appropriate input
|
||||
if (tabName === 'case-create') {
|
||||
document.getElementById('newCaseName').focus();
|
||||
} else {
|
||||
document.getElementById('linkCaseName').focus();
|
||||
}
|
||||
}
|
||||
|
||||
closeCreateCaseModal() {
|
||||
document.getElementById('createCaseModal').classList.remove('active');
|
||||
}
|
||||
|
||||
async submitCaseModal() {
|
||||
if (this.caseModalTab === 'case-create') {
|
||||
await this.createCase();
|
||||
} else {
|
||||
await this.linkCase();
|
||||
}
|
||||
}
|
||||
|
||||
async createCase() {
|
||||
const name = document.getElementById('newCaseName').value.trim();
|
||||
const description = document.getElementById('newCaseDescription').value.trim();
|
||||
|
||||
if (!name) {
|
||||
this.toast('Please enter a case name', 'error');
|
||||
this.showToast('Please enter a case name', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
||||
this.toast('Invalid name. Use only letters, numbers, hyphens, underscores.', 'error');
|
||||
this.showToast('Invalid name. Use only letters, numbers, hyphens, underscores.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2667,15 +2708,56 @@ class ClaudemanApp {
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
this.closeCreateCaseModal();
|
||||
this.toast(`Case "${name}" created`, 'success');
|
||||
this.showToast(`Case "${name}" created`, 'success');
|
||||
// Reload cases and select the new one
|
||||
await this.loadQuickStartCases(name);
|
||||
} else {
|
||||
this.toast(data.error || 'Failed to create case', 'error');
|
||||
this.showToast(data.error || 'Failed to create case', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to create case:', err);
|
||||
this.toast('Failed to create case: ' + err.message, 'error');
|
||||
this.showToast('Failed to create case: ' + err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async linkCase() {
|
||||
const name = document.getElementById('linkCaseName').value.trim();
|
||||
const path = document.getElementById('linkCasePath').value.trim();
|
||||
|
||||
if (!name) {
|
||||
this.showToast('Please enter a case name', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
||||
this.showToast('Invalid name. Use only letters, numbers, hyphens, underscores.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
this.showToast('Please enter a folder path', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/cases/link', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, path })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
this.closeCreateCaseModal();
|
||||
this.showToast(`Case "${name}" linked to ${path}`, 'success');
|
||||
// Reload cases and select the new one
|
||||
await this.loadQuickStartCases(name);
|
||||
} else {
|
||||
this.showToast(data.error || 'Failed to link case', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to link case:', err);
|
||||
this.showToast('Failed to link case: ' + err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-10
@@ -537,25 +537,45 @@
|
||||
<!-- Create Case Modal -->
|
||||
<div class="modal" id="createCaseModal">
|
||||
<div class="modal-backdrop" onclick="app.closeCreateCaseModal()"></div>
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-content modal-lg">
|
||||
<div class="modal-header">
|
||||
<h3>Create New Case</h3>
|
||||
<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">
|
||||
<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</span>
|
||||
<!-- 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>
|
||||
<div class="form-row">
|
||||
<label>Description (optional)</label>
|
||||
<input type="text" id="newCaseDescription" placeholder="A brief description...">
|
||||
<!-- 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" onclick="app.createCase()">Create</button>
|
||||
<button class="btn-toolbar btn-primary" id="caseModalSubmit" onclick="app.submitCaseModal()">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user