continued improvements
This commit is contained in:
@@ -643,34 +643,53 @@ function renderImages(images) {
|
||||
imagesList.querySelectorAll('.action-remove-image').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const imageId = btn.dataset.imageId;
|
||||
if (confirm('Are you sure you want to remove this image?')) {
|
||||
showConfirmModal('Are you sure you want to remove this image?', () => {
|
||||
sendCommand('removeImage', { id: imageId, force: true });
|
||||
setTimeout(() => loadImages(), 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
imagesList.querySelectorAll('.action-tag-image').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const imageId = btn.dataset.imageId;
|
||||
const repo = prompt('Enter repository name (e.g., myrepo):');
|
||||
if (repo) {
|
||||
const tag = prompt('Enter tag (default: latest):', 'latest') || 'latest';
|
||||
|
||||
showStatusIndicator(`Tagging image...`);
|
||||
sendCommand('tagImage', { id: imageId, repo, tag });
|
||||
|
||||
try {
|
||||
const response = await waitForPeerResponse('Image tagged as');
|
||||
showAlert('success', response.message || 'Image tagged successfully');
|
||||
loadImages();
|
||||
} catch (error) {
|
||||
console.error('[ERROR] Failed to tag image:', error);
|
||||
showAlert('danger', error.message || 'Failed to tag image');
|
||||
} finally {
|
||||
hideStatusIndicator();
|
||||
const modal = new bootstrap.Modal(document.getElementById('tagImageModal'));
|
||||
const repoInput = document.getElementById('tag-repo');
|
||||
const tagInput = document.getElementById('tag-tag');
|
||||
const confirmBtn = document.getElementById('confirm-tag-btn');
|
||||
|
||||
repoInput.value = '';
|
||||
tagInput.value = 'latest';
|
||||
|
||||
// Remove old listeners
|
||||
const newConfirmBtn = confirmBtn.cloneNode(true);
|
||||
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
|
||||
|
||||
newConfirmBtn.addEventListener('click', async () => {
|
||||
const repo = repoInput.value.trim();
|
||||
if (repo) {
|
||||
const tag = tagInput.value.trim() || 'latest';
|
||||
|
||||
modal.hide();
|
||||
showStatusIndicator(`Tagging image...`);
|
||||
sendCommand('tagImage', { id: imageId, repo, tag });
|
||||
|
||||
try {
|
||||
const response = await waitForPeerResponse('Image tagged as');
|
||||
showAlert('success', response.message || 'Image tagged successfully');
|
||||
loadImages();
|
||||
} catch (error) {
|
||||
console.error('[ERROR] Failed to tag image:', error);
|
||||
showAlert('danger', error.message || 'Failed to tag image');
|
||||
} finally {
|
||||
hideStatusIndicator();
|
||||
}
|
||||
} else {
|
||||
showAlert('danger', 'Repository name is required');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modal.show();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -735,10 +754,10 @@ function renderNetworks(networks) {
|
||||
networksList.querySelectorAll('.action-remove-network').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const networkId = btn.dataset.networkId;
|
||||
if (confirm('Are you sure you want to remove this network?')) {
|
||||
showConfirmModal('Are you sure you want to remove this network?', () => {
|
||||
sendCommand('removeNetwork', { id: networkId });
|
||||
setTimeout(() => loadNetworks(), 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -752,6 +771,13 @@ function renderNetworks(networks) {
|
||||
networksList.querySelectorAll('.action-connect-network').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const networkId = btn.dataset.networkId;
|
||||
const modal = new bootstrap.Modal(document.getElementById('connectNetworkModal'));
|
||||
const containerSelect = document.getElementById('connect-container-select');
|
||||
const confirmBtn = document.getElementById('confirm-connect-btn');
|
||||
|
||||
// Store networkId for later use
|
||||
modal._networkId = networkId;
|
||||
|
||||
// Get list of containers for selection
|
||||
sendCommand('listContainers');
|
||||
|
||||
@@ -762,18 +788,32 @@ function renderNetworks(networks) {
|
||||
const containers = response.data;
|
||||
if (containers.length === 0) {
|
||||
showAlert('warning', 'No containers available to connect');
|
||||
if (typeof originalHandler === 'function') {
|
||||
window.handlePeerResponse = originalHandler;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a simple selection dialog
|
||||
const containerNames = containers.map(c => c.Names[0]?.replace(/^\//, '') || c.Id.substring(0, 12));
|
||||
const selectedName = prompt(`Enter container name to connect to network:\n\nAvailable: ${containerNames.slice(0, 5).join(', ')}${containerNames.length > 5 ? '...' : ''}`, containerNames[0]);
|
||||
// Populate select dropdown
|
||||
containerSelect.innerHTML = '<option value="">Select a container...</option>';
|
||||
containers.forEach(container => {
|
||||
const name = container.Names[0]?.replace(/^\//, '') || container.Id.substring(0, 12);
|
||||
const option = document.createElement('option');
|
||||
option.value = container.Id;
|
||||
option.textContent = `${name} (${container.State})`;
|
||||
containerSelect.appendChild(option);
|
||||
});
|
||||
|
||||
if (selectedName) {
|
||||
const container = containers.find(c => c.Names[0]?.replace(/^\//, '') === selectedName || c.Id.startsWith(selectedName));
|
||||
if (container) {
|
||||
// Remove old listeners
|
||||
const newConfirmBtn = confirmBtn.cloneNode(true);
|
||||
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
|
||||
|
||||
newConfirmBtn.addEventListener('click', async () => {
|
||||
const containerId = containerSelect.value;
|
||||
if (containerId) {
|
||||
modal.hide();
|
||||
showStatusIndicator(`Connecting container to network...`);
|
||||
sendCommand('connectNetwork', { networkId, containerId: container.Id });
|
||||
sendCommand('connectNetwork', { networkId: modal._networkId, containerId });
|
||||
|
||||
try {
|
||||
const response = await waitForPeerResponse('Container connected to network');
|
||||
@@ -786,9 +826,11 @@ function renderNetworks(networks) {
|
||||
hideStatusIndicator();
|
||||
}
|
||||
} else {
|
||||
showAlert('danger', 'Container not found');
|
||||
showAlert('danger', 'Please select a container');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modal.show();
|
||||
|
||||
if (typeof originalHandler === 'function') {
|
||||
window.handlePeerResponse = originalHandler;
|
||||
@@ -846,7 +888,20 @@ function renderStacks(stacks) {
|
||||
stacksListBody.querySelectorAll('.action-remove-stack').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const stackName = btn.dataset.stackName;
|
||||
if (!confirm(`Remove stack "${stackName}"? This will remove all containers in the stack.`)) return;
|
||||
let confirmed = false;
|
||||
await new Promise((resolve) => {
|
||||
showConfirmModal(`Remove stack "${stackName}"? This will remove all containers in the stack.`, () => {
|
||||
confirmed = true;
|
||||
resolve();
|
||||
});
|
||||
const modalEl = document.getElementById('confirmModal');
|
||||
if (modalEl) {
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
if (!confirmed) resolve();
|
||||
}, { once: true });
|
||||
}
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
showStatusIndicator(`Removing stack "${stackName}"...`);
|
||||
sendCommand('removeStack', { stackName });
|
||||
@@ -948,10 +1003,10 @@ function renderVolumes(volumes) {
|
||||
volumesList.querySelectorAll('.action-remove-volume').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const volumeName = btn.dataset.volumeName;
|
||||
if (confirm('Are you sure you want to remove this volume? This cannot be undone.')) {
|
||||
showConfirmModal('Are you sure you want to remove this volume? This cannot be undone.', () => {
|
||||
sendCommand('removeVolume', { name: volumeName });
|
||||
setTimeout(() => loadVolumes(), 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2395,9 +2450,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', () => {
|
||||
const logsContent = document.getElementById('container-logs-content');
|
||||
if (logsContent && confirm('Clear all logs?')) {
|
||||
logsContent.innerHTML = '';
|
||||
logsState.allLogs = [];
|
||||
if (logsContent) {
|
||||
showConfirmModal('Clear all logs?', () => {
|
||||
logsContent.innerHTML = '';
|
||||
logsState.allLogs = [];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2757,6 +2814,28 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
});
|
||||
|
||||
// Helper function for confirmation modals
|
||||
function showConfirmModal(message, onConfirm) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('confirmModal'));
|
||||
const messageEl = document.getElementById('confirmModalMessage');
|
||||
const confirmBtn = document.getElementById('confirmModalBtn');
|
||||
|
||||
messageEl.textContent = message;
|
||||
|
||||
// Remove old listeners
|
||||
const newConfirmBtn = confirmBtn.cloneNode(true);
|
||||
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
|
||||
|
||||
newConfirmBtn.addEventListener('click', () => {
|
||||
modal.hide();
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm();
|
||||
}
|
||||
});
|
||||
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Bulk Operations Functions
|
||||
function getSelectedContainers() {
|
||||
const checkboxes = document.querySelectorAll('.container-checkbox:checked');
|
||||
@@ -2843,7 +2922,9 @@ async function bulkStartContainers() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Start ${selected.length} container(s)?`)) return;
|
||||
await new Promise((resolve) => {
|
||||
showConfirmModal(`Start ${selected.length} container(s)?`, resolve);
|
||||
});
|
||||
|
||||
showStatusIndicator(`Starting ${selected.length} container(s)...`);
|
||||
|
||||
@@ -2871,7 +2952,20 @@ async function bulkStopContainers() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Stop ${selected.length} container(s)?`)) return;
|
||||
let confirmed = false;
|
||||
await new Promise((resolve) => {
|
||||
showConfirmModal(`Stop ${selected.length} container(s)?`, () => {
|
||||
confirmed = true;
|
||||
resolve();
|
||||
});
|
||||
const modalEl = document.getElementById('confirmModal');
|
||||
if (modalEl) {
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
if (!confirmed) resolve();
|
||||
}, { once: true });
|
||||
}
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
showStatusIndicator(`Stopping ${selected.length} container(s)...`);
|
||||
|
||||
@@ -2899,7 +2993,20 @@ async function bulkRemoveContainers() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Remove ${selected.length} container(s)? This action cannot be undone.`)) return;
|
||||
let confirmed = false;
|
||||
await new Promise((resolve) => {
|
||||
showConfirmModal(`Remove ${selected.length} container(s)? This action cannot be undone.`, () => {
|
||||
confirmed = true;
|
||||
resolve();
|
||||
});
|
||||
const modalEl = document.getElementById('confirmModal');
|
||||
if (modalEl) {
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
if (!confirmed) resolve();
|
||||
}, { once: true });
|
||||
}
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
showStatusIndicator(`Removing ${selected.length} container(s)...`);
|
||||
|
||||
@@ -2940,7 +3047,20 @@ async function bulkRemoveImages() {
|
||||
const selected = getSelectedImages();
|
||||
if (selected.length === 0) return;
|
||||
|
||||
if (!confirm(`Remove ${selected.length} image(s)? This action cannot be undone.`)) return;
|
||||
let confirmed = false;
|
||||
await new Promise((resolve) => {
|
||||
showConfirmModal(`Remove ${selected.length} image(s)? This action cannot be undone.`, () => {
|
||||
confirmed = true;
|
||||
resolve();
|
||||
});
|
||||
const modalEl = document.getElementById('confirmModal');
|
||||
if (modalEl) {
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
if (!confirmed) resolve();
|
||||
}, { once: true });
|
||||
}
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
showStatusIndicator(`Removing ${selected.length} image(s)...`);
|
||||
let completed = 0;
|
||||
@@ -4408,7 +4528,15 @@ function renderContainers(containers, topicId) {
|
||||
<td>
|
||||
<input type="checkbox" class="container-checkbox" data-container-id="${containerId}">
|
||||
</td>
|
||||
<td><a href="#" class="container-name-link" data-container-id="${containerId}">${name}</a></td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="container-name-display" data-container-id="${containerId}" style="cursor: pointer; user-select: none;">${name}</span>
|
||||
<button class="btn btn-outline-info action-rename p-1" title="Rename" style="font-size: 0.75rem;">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<a href="#" class="container-name-link d-none" data-container-id="${containerId}">${name}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>${image}</td>
|
||||
<td><span class="badge ${statusClass}">${state}</span></td>
|
||||
<td class="cpu">
|
||||
@@ -4442,33 +4570,18 @@ function renderContainers(containers, topicId) {
|
||||
<button class="btn btn-outline-secondary action-pause p-1" title="Pause" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||
<i class="fas fa-pause"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary action-unpause p-1" title="Unpause" ${container.State !== 'paused' ? 'disabled' : ''}>
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-primary action-logs p-1" title="Logs">
|
||||
<i class="fas fa-list-alt"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-primary action-terminal p-1" title="Terminal" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||
<i class="fas fa-terminal"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-success action-details p-1" title="View Details">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-info action-inspect p-1" title="Inspect">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary action-duplicate p-1" title="Duplicate">
|
||||
<i class="fas fa-clone"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-info action-rename p-1" title="Rename">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-success action-commit p-1" title="Commit" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||
<i class="fas fa-save"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-primary action-exec p-1" title="Exec" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||
<i class="fas fa-code"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-danger action-remove p-1" title="Remove">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@@ -4485,12 +4598,7 @@ function renderContainers(containers, topicId) {
|
||||
// Add event listener for duplicate button
|
||||
const duplicateBtn = row.querySelector('.action-duplicate');
|
||||
duplicateBtn.addEventListener('click', () => openDuplicateModal(container));
|
||||
// Add event listener for details button
|
||||
const detailsBtn = row.querySelector('.action-details');
|
||||
if (detailsBtn) {
|
||||
detailsBtn.addEventListener('click', () => showContainerDetails(container));
|
||||
}
|
||||
// Add event listener for clickable container name
|
||||
// Add event listener for clickable container name (hidden link for details view)
|
||||
const nameLink = row.querySelector('.container-name-link');
|
||||
if (nameLink) {
|
||||
nameLink.addEventListener('click', (e) => {
|
||||
@@ -4498,6 +4606,15 @@ function renderContainers(containers, topicId) {
|
||||
showContainerDetails(container);
|
||||
});
|
||||
}
|
||||
|
||||
// Add event listener for container name display (double-click to view details)
|
||||
const nameDisplay = row.querySelector('.container-name-display');
|
||||
if (nameDisplay) {
|
||||
nameDisplay.addEventListener('dblclick', (e) => {
|
||||
e.preventDefault();
|
||||
showContainerDetails(container);
|
||||
});
|
||||
}
|
||||
// Add event listeners for action buttons
|
||||
addActionListeners(row, container);
|
||||
});
|
||||
@@ -4513,7 +4630,6 @@ function addActionListeners(row, container) {
|
||||
const startBtn = row.querySelector('.action-start');
|
||||
const stopBtn = row.querySelector('.action-stop');
|
||||
const pauseBtn = row.querySelector('.action-pause');
|
||||
const unpauseBtn = row.querySelector('.action-unpause');
|
||||
const removeBtn = row.querySelector('.action-remove');
|
||||
const terminalBtn = row.querySelector('.action-terminal');
|
||||
const restartBtn = row.querySelector('.action-restart');
|
||||
@@ -4623,107 +4739,116 @@ function addActionListeners(row, container) {
|
||||
});
|
||||
}
|
||||
|
||||
// Unpause Button
|
||||
if (unpauseBtn) {
|
||||
unpauseBtn.addEventListener('click', async () => {
|
||||
showStatusIndicator(`Unpausing container "${container.Names[0]}"...`);
|
||||
sendCommand('unpauseContainer', { id: container.Id });
|
||||
|
||||
const expectedMessageFragment = `Container ${container.Id} unpaused`;
|
||||
|
||||
try {
|
||||
const response = await waitForPeerResponse(expectedMessageFragment);
|
||||
showAlert('success', response.message);
|
||||
sendCommand('listContainers');
|
||||
} catch (error) {
|
||||
console.error('[ERROR] Failed to unpause container:', error.message);
|
||||
showAlert('danger', error.message || 'Failed to unpause container.');
|
||||
} finally {
|
||||
hideStatusIndicator();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Rename Button
|
||||
// Rename Button - Inline Edit
|
||||
if (renameBtn) {
|
||||
renameBtn.addEventListener('click', () => {
|
||||
const currentName = container.Names[0]?.replace(/^\//, '') || '';
|
||||
const newName = prompt(`Rename container "${currentName}":`, currentName);
|
||||
if (newName && newName !== currentName) {
|
||||
showStatusIndicator(`Renaming container...`);
|
||||
sendCommand('renameContainer', { id: container.Id, name: newName });
|
||||
const nameDisplay = row.querySelector('.container-name-display');
|
||||
if (!nameDisplay) return;
|
||||
|
||||
const currentName = nameDisplay.textContent.trim();
|
||||
const originalName = currentName;
|
||||
|
||||
// Create input field
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = currentName;
|
||||
input.className = 'form-control form-control-sm bg-dark text-white';
|
||||
input.style.width = '200px';
|
||||
input.style.display = 'inline-block';
|
||||
|
||||
// Replace display with input
|
||||
const parent = nameDisplay.parentElement;
|
||||
const nameDisplayClone = nameDisplay.cloneNode(true);
|
||||
nameDisplay.style.display = 'none';
|
||||
parent.insertBefore(input, nameDisplay);
|
||||
|
||||
// Focus and select
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
// Save function
|
||||
const saveName = async () => {
|
||||
const newName = input.value.trim();
|
||||
if (newName && newName !== originalName) {
|
||||
sendCommand('renameContainer', { id: container.Id, name: newName });
|
||||
|
||||
const expectedMessageFragment = `Container renamed to "${newName}"`;
|
||||
const expectedMessageFragment = `Container renamed to "${newName}"`;
|
||||
|
||||
waitForPeerResponse(expectedMessageFragment).then(response => {
|
||||
showAlert('success', response.message);
|
||||
sendCommand('listContainers');
|
||||
}).catch(error => {
|
||||
console.error('[ERROR] Failed to rename container:', error.message);
|
||||
showAlert('danger', error.message || 'Failed to rename container.');
|
||||
}).finally(() => {
|
||||
hideStatusIndicator();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Set up response handler
|
||||
const originalHandler = window.handlePeerResponse;
|
||||
const responseHandler = (response) => {
|
||||
if (response && response.success && response.message && response.message.includes(expectedMessageFragment)) {
|
||||
showAlert('success', response.message);
|
||||
sendCommand('listContainers');
|
||||
// Restore original handler
|
||||
if (typeof originalHandler === 'function') {
|
||||
window.handlePeerResponse = originalHandler;
|
||||
} else {
|
||||
window.handlePeerResponse = null;
|
||||
}
|
||||
} else if (typeof originalHandler === 'function') {
|
||||
originalHandler(response);
|
||||
}
|
||||
};
|
||||
|
||||
window.handlePeerResponse = responseHandler;
|
||||
|
||||
// Commit Button
|
||||
if (commitBtn) {
|
||||
commitBtn.addEventListener('click', () => {
|
||||
const currentName = container.Names[0]?.replace(/^\//, '') || 'container';
|
||||
const repo = prompt(`Enter repository name for commit:`, currentName);
|
||||
if (repo) {
|
||||
const tag = prompt(`Enter tag (default: latest):`, 'latest') || 'latest';
|
||||
const message = prompt(`Enter commit message (optional):`, '') || '';
|
||||
// Also listen for container list updates as confirmation
|
||||
const checkInterval = setInterval(() => {
|
||||
const nameDisplays = document.querySelectorAll('.container-name-display');
|
||||
nameDisplays.forEach(display => {
|
||||
if (display.textContent.trim() === newName && display.dataset.containerId === container.Id) {
|
||||
clearInterval(checkInterval);
|
||||
showAlert('success', `Container renamed to "${newName}"`);
|
||||
// Restore original handler
|
||||
if (typeof originalHandler === 'function') {
|
||||
window.handlePeerResponse = originalHandler;
|
||||
} else {
|
||||
window.handlePeerResponse = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
|
||||
// Clean up interval after 10 seconds
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
}, 10000);
|
||||
|
||||
} else if (!newName) {
|
||||
showAlert('danger', 'Container name cannot be empty');
|
||||
nameDisplay.textContent = originalName;
|
||||
} else {
|
||||
// Name unchanged, just restore display
|
||||
nameDisplay.textContent = originalName;
|
||||
}
|
||||
|
||||
showStatusIndicator(`Committing container...`);
|
||||
sendCommand('commitContainer', {
|
||||
id: container.Id,
|
||||
repo: repo,
|
||||
tag: tag,
|
||||
message: message
|
||||
});
|
||||
|
||||
waitForPeerResponse('committed').then(response => {
|
||||
showAlert('success', response.message || 'Container committed successfully');
|
||||
sendCommand('listImages');
|
||||
}).catch(error => {
|
||||
console.error('[ERROR] Failed to commit container:', error.message);
|
||||
showAlert('danger', error.message || 'Failed to commit container.');
|
||||
}).finally(() => {
|
||||
hideStatusIndicator();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Exec Button
|
||||
if (execBtn) {
|
||||
execBtn.addEventListener('click', () => {
|
||||
const command = prompt(`Enter command to execute in container:`, '/bin/sh');
|
||||
if (command) {
|
||||
showStatusIndicator(`Executing command...`);
|
||||
sendCommand('execContainer', {
|
||||
id: container.Id,
|
||||
cmd: command.split(' '),
|
||||
tty: true
|
||||
});
|
||||
|
||||
// Open terminal for exec output
|
||||
waitForPeerResponse('Exec session started').then(response => {
|
||||
if (response.execId) {
|
||||
// Start terminal session for exec - use regular terminal for now
|
||||
startTerminal(container.Id, `Exec: ${container.Names[0]?.replace(/^\//, '') || container.Id.substring(0, 12)}`);
|
||||
showAlert('success', 'Exec session started');
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('[ERROR] Failed to exec container:', error.message);
|
||||
showAlert('danger', error.message || 'Failed to exec container.');
|
||||
}).finally(() => {
|
||||
hideStatusIndicator();
|
||||
});
|
||||
}
|
||||
// Restore display
|
||||
parent.removeChild(input);
|
||||
nameDisplay.style.display = '';
|
||||
};
|
||||
|
||||
// Cancel function
|
||||
const cancelEdit = () => {
|
||||
parent.removeChild(input);
|
||||
nameDisplay.style.display = '';
|
||||
};
|
||||
|
||||
// Handle Enter key
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
saveName();
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
cancelEdit();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle blur (click outside)
|
||||
input.addEventListener('blur', () => {
|
||||
saveName();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5667,11 +5792,11 @@ function initNotificationTray() {
|
||||
|
||||
if (clearAllBtn) {
|
||||
clearAllBtn.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to clear all notifications?')) {
|
||||
showConfirmModal('Are you sure you want to clear all notifications?', () => {
|
||||
notificationManager.clearAll();
|
||||
renderNotifications();
|
||||
updateBadge();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user