Custom Commands: Typen (Hintergrund/Vordergrund/Nachricht) mit Output-Modal

This commit is contained in:
Meleki committed 2026-02-28 21:30:15 +01:00
1 parent d83a6f60ef
commit ab234fde69
8 files changed
+241 -36

No files matched your search

+5 -3
View File
@@ -104,12 +104,14 @@ async function apiUpdateSession(sessionId, data) {
return resp.json();
}
async function apiExecCommand(sessionId, command) {
return fetch(`/api/sessions/${sessionId}/exec`, {
async function apiExecCommand(sessionId, command, mode = 'background') {
const resp = await fetch(`/api/sessions/${sessionId}/exec`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command })
body: JSON.stringify({ command, mode })
});
if (mode === 'foreground') return resp.json();
return resp;
}
async function apiGetSystemPrompt(sessionId) {
+21 -6
View File
@@ -65,22 +65,37 @@ registerPage('chat', {
try { cmds = JSON.parse(session.customCommands || '[]') || []; } catch(e) {}
cmds.forEach(cmd => {
if (!cmd.name || !cmd.command) return;
const type = cmd.type || 'background';
const btn = document.createElement('button');
btn.className = 'custom-cmd-btn';
if (type === 'message') btn.classList.add('cmd-type-message');
btn.textContent = cmd.name;
btn.title = cmd.command;
btn.title = type === 'message' ? cmd.command : `[${type}] ${cmd.command}`;
btn.addEventListener('click', async () => {
if (type === 'message') {
// Nachricht in den Chat senden
const inputEl = document.getElementById('input');
if (inputEl) {
inputEl.value = cmd.command;
inputEl.dispatchEvent(new Event('input'));
}
sendMessage();
return;
}
btn.disabled = true;
btn.classList.add('running');
try {
await apiExecCommand(sessionId, cmd.command);
if (type === 'foreground') {
const result = await apiExecCommand(sessionId, cmd.command, 'foreground');
showCommandOutputModal(cmd.name, cmd.command, result);
} else {
await apiExecCommand(sessionId, cmd.command, 'background');
}
} catch(e) {
console.error('Exec failed:', e);
}
setTimeout(() => {
btn.disabled = false;
btn.classList.remove('running');
}, 1000);
btn.disabled = false;
btn.classList.remove('running');
});
cmdBar.appendChild(btn);
});
+16 -4
View File
@@ -3,16 +3,27 @@
let _sessionImageData = null;
let _sessionPasteHandler = null;
function _createCommandRow(container, name = '', command = '') {
function _createCommandRow(container, name = '', command = '', type = 'background') {
const item = document.createElement('div');
item.className = 'custom-command-item';
item.innerHTML = `
<div class="custom-command-fields">
<select class="cmd-type">
<option value="background">Hintergrund</option>
<option value="foreground">Vordergrund</option>
<option value="message">Nachricht</option>
</select>
<input type="text" class="cmd-name" placeholder="Name (z.B. Deploy)" value="${_escAttr(name)}">
<input type="text" class="cmd-command" placeholder="Befehl (z.B. ./deploy.sh)" value="${_escAttr(command)}">
<input type="text" class="cmd-command" placeholder="${type === 'message' ? 'Nachricht...' : 'Befehl (z.B. ./deploy.sh)'}" value="${_escAttr(command)}">
</div>
<button type="button" class="remove-cmd-btn icon-btn"><i class="fa fa-times"></i></button>
`;
const typeSelect = item.querySelector('.cmd-type');
typeSelect.value = type;
typeSelect.addEventListener('change', () => {
const cmdInput = item.querySelector('.cmd-command');
cmdInput.placeholder = typeSelect.value === 'message' ? 'Nachricht...' : 'Befehl (z.B. ./deploy.sh)';
});
item.querySelector('.remove-cmd-btn').addEventListener('click', () => item.remove());
container.appendChild(item);
}
@@ -27,7 +38,8 @@ function _getCommandsFromUI(container) {
for (const item of items) {
const name = item.querySelector('.cmd-name').value.trim();
const command = item.querySelector('.cmd-command').value.trim();
if (name || command) commands.push({ name, command });
const type = item.querySelector('.cmd-type').value;
if (name || command) commands.push({ name, command, type });
}
return commands.length > 0 ? commands : null;
}
@@ -83,7 +95,7 @@ registerPage('session', {
? JSON.parse(session.customCommands)
: session.customCommands;
for (const cmd of cmds) {
_createCommandRow(commandsList, cmd.name || '', cmd.command || '');
_createCommandRow(commandsList, cmd.name || '', cmd.command || '', cmd.type || 'background');
}
} catch (e) { /* ignore parse errors */ }
}
+55
View File
@@ -461,6 +461,61 @@ async function showSystemPromptModal(sessionId) {
}
}
// ── Command Output Modal ──
function showCommandOutputModal(name, command, result) {
const overlay = document.createElement('div');
overlay.className = 'sp-overlay';
const modal = document.createElement('div');
modal.className = 'sp-modal cmd-output-modal';
// Header
const header = document.createElement('div');
header.className = 'sp-header';
const title = document.createElement('span');
title.textContent = name;
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
closeBtn.className = 'sp-close icon-btn';
header.appendChild(title);
header.appendChild(closeBtn);
// Body
const body = document.createElement('div');
body.className = 'sp-body';
const exitCode = result.exitCode != null ? result.exitCode : '?';
const isError = exitCode !== 0;
const statusText = result.status === 'timeout' ? 'Timeout' : (isError ? `Exit ${exitCode}` : `Exit ${exitCode}`);
const meta = document.createElement('div');
meta.className = 'cmd-output-meta';
meta.innerHTML = `<code>${esc(command)}</code> <span class="cmd-exit-code${isError ? ' cmd-exit-error' : ''}">${esc(statusText)}</span>`;
body.appendChild(meta);
const output = document.createElement('pre');
output.className = 'cmd-output-content';
output.textContent = result.output || '(kein Output)';
body.appendChild(output);
modal.appendChild(header);
modal.appendChild(body);
overlay.appendChild(modal);
document.body.appendChild(overlay);
// Schließen
const close = () => overlay.remove();
closeBtn.addEventListener('click', close);
overlay.addEventListener('click', (e) => {
if (e.target === overlay) close();
});
const onKey = (e) => {
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
};
document.addEventListener('keydown', onKey);
}
// ── Meta Bar ──
function setMeta(text) {