Files
2024-12-15 10:51:32 +02:00

382 lines
15 KiB
JavaScript

async function checkAndShowMissingPackagesDialog(missingCustomPackages, missingNodes, flowConfig) {
const combinedMissingCustomPackages = enrichMissingCustomPackages(missingCustomPackages, flowConfig);
const filteredMissingCustomPackages = combinedMissingCustomPackages;
if (filteredMissingCustomPackages.length === 0 && missingNodes.length === 0) {
return;
}
showMissingPackagesDialog(filteredMissingCustomPackages, missingNodes);
}
function enrichMissingCustomPackages(missingCustomPackages, flowConfig) {
if (!flowConfig.configCustomNodes) {
return missingCustomPackages;
}
const customNodesByTitle = {};
for (const nodeConfig of flowConfig.configCustomNodes) {
customNodesByTitle[nodeConfig.title] = nodeConfig;
}
const enrichedPackages = missingCustomPackages.map(pkg => {
let title;
if (typeof pkg === 'string') {
title = pkg;
} else {
title = pkg.title;
}
const nodeConfig = customNodesByTitle[title];
if (nodeConfig) {
return {
title: nodeConfig.title,
packageUrl: nodeConfig.installUrl,
downloadModal: nodeConfig.downloadModal || {},
};
} else {
return pkg;
}
});
for (const nodeConfig of flowConfig.configCustomNodes) {
if (!enrichedPackages.some(pkg => pkg.title === nodeConfig.title)) {
enrichedPackages.push({
title: nodeConfig.title,
packageUrl: nodeConfig.installUrl,
downloadModal: nodeConfig.downloadModal || {},
});
}
}
const uniquePackages = [];
const seenTitles = new Set();
for (const pkg of enrichedPackages) {
if (!seenTitles.has(pkg.title)) {
seenTitles.add(pkg.title);
uniquePackages.push(pkg);
}
}
return uniquePackages;
}
async function fetchInstalledCustomNodes() {
try {
const response = await fetch('/flow/api/installed-custom-nodes');
if (response.ok) {
const data = await response.json();
return data.installedNodes || [];
} else {
console.error('Failed to fetch installed custom nodes');
return [];
}
} catch (error) {
console.error('Error fetching installed custom nodes:', error);
return [];
}
}
function showMissingPackagesDialog(missingCustomPackages, missingNodes) {
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
`;
const dialog = document.createElement('div');
dialog.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #1e1e1e;
color: #ffffff;
padding: 24px;
border: 2px dashed #ff4444;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
border-radius: 5px;
z-index: 1000;
min-width: 700px;
width: 45%;
max-height: 95vh;
overflow-y: auto;
font-family: Arial, sans-serif;
`;
const scrollbarStyles = `
<style>
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: #121212 #2a2a2a;
padding-right: 4px;
}
.custom-scrollbar::-webkit-scrollbar {
width: 10px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #2a2a2a;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color:#121212;
border: 2px solid #1e1e1f;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #8621b7;
}
.disabled-button {
opacity: 0.5;
cursor: not-allowed;
}
</style>
`;
let content = `
${scrollbarStyles}
<h2 style="margin-top: 0; color: #ff4444; font-size: 24px; border-bottom: 1px dashed #ff4444; padding-bottom: 12px; text-align: center;">
Nodes Unavailable
</h2>
<h4 style="margin-top: 0; color: #ff8c44; border-bottom: 1px dashed #ff4444; padding-top: 12px; padding-bottom: 12px; text-align: center;">
Some custom nodes require manual installation. Follow the instructions on the custom node page.
</h4>
`;
if (missingCustomPackages.length > 0) {
content += `
<div style="margin-top: 20px;">
<h3 style="color: #ffa500; font-size: 18px; padding-bottom: 12px;">${missingCustomPackages.length} Missing Custom Nodes:</h3>
<div class="custom-scrollbar" style="max-height: 500px; overflow-y: auto;">
<ul style="list-style-type: none; padding-left: 0;">
${missingCustomPackages.map(pkg => `
<li style="background: #2a2a2a;color: #ff4444; margin-bottom: 8px; padding: 8px 12px; ">
<div style="display: flex; align-items: center; justify-content: space-between;">
<strong style="font-size: 22px;">${pkg.title}</strong>
<div>
<button class="install-btn" data-url="${pkg.packageUrl || ''}" data-download-modal='${JSON.stringify(pkg.downloadModal || {})}' style="
margin-left: 10px; padding: 8px 12px; color: #28a745; background: #121212; border: none; cursor: pointer; transition: 0.3s;">
Install
</button>
<button class="update-btn" data-url="${pkg.packageUrl || ''}" style="
margin-left: 10px; padding: 8px 12px; color: #ffc107; background: #121212; border: none; cursor: pointer; transition: 0.3s;">
Update
</button>
<button class="uninstall-btn" data-url="${pkg.packageUrl || ''}" style="
margin-left: 10px; padding: 8px 12px; color: #dc3545; background: #121212; border: none; cursor: pointer; transition: 0.3s;">
Uninstall
</button>
</div>
</div>
${pkg.packageUrl ? `
<div style="margin-top: 8px; color: #1e90ff;">
<a href="${pkg.packageUrl}" target="_blank" style="color: #1e90ff; text-decoration: none;">
${pkg.packageUrl}
</a>
</div>
` : pkg.downloadModal && pkg.downloadModal.downloadUrl ? `
<div style="margin-top: 8px; color: #1e90ff;">
<a href="${pkg.downloadModal.downloadUrl}" target="_blank" style="color: #1e90ff; text-decoration: none;">
${pkg.downloadModal.downloadUrl}
</a>
</div>
` : ''}
<div class="status-message" style="margin-top: 8px; color: #28a745;"></div>
</li>
`).join('')}
</ul>
</div>
</div>
`;
}
if (missingNodes.length > 0) {
content += `
<div style="margin-top: 20px;">
<h3 style="color: #ffa500; font-size: 18px; cursor: pointer;" id="toggle-missing-nodes">
${missingNodes.length} Missing Nodes: (Click to toggle)
</h3>
<div id="missing-nodes-list" class="custom-scrollbar" style="max-height: 100px; overflow-y: auto; display: none;">
<ul style="list-style-type: none; padding-left: 0;">
${missingNodes.map(node => `
<li style="background: #2a2a2a; margin-bottom: 8px; padding: 4px 12px;">
${node}
</li>
`).join('')}
</ul>
</div>
</div>
`;
}
content += `
<div style="text-align: center; margin-top: 20px;">
<a href="/flow" style="
display: inline-block;
padding: 10px 20px;
background-color: #121212;
color: #8621b7;
text-decoration: none;
font-weight: bold;
transition: background-color 0.3s;
">Return to Menu</a>
</div>
`;
dialog.innerHTML = content;
dialog.classList.add('custom-scrollbar');
document.body.appendChild(overlay);
document.body.appendChild(dialog);
const allActionButtons = document.querySelectorAll('.install-btn, .update-btn, .uninstall-btn');
allActionButtons.forEach(button => {
button.addEventListener('mouseover', function() {
if (!this.disabled) {
const currentBg = getComputedStyle(this).backgroundColor;
const currentTextColor = getComputedStyle(this).color;
this.style.backgroundColor = currentTextColor;
this.style.color = currentBg;
}
});
button.addEventListener('mouseout', function() {
if (!this.disabled) {
let originalColor;
if (this.classList.contains('install-btn')) {
originalColor = '#28a745'; // Green
} else if (this.classList.contains('update-btn')) {
originalColor = '#ffc107'; // Yellow
} else if (this.classList.contains('uninstall-btn')) {
originalColor = '#dc3545'; // Red
}
this.style.backgroundColor = '#121212';
this.style.color = originalColor;
}
});
});
document.querySelectorAll('.install-btn').forEach(button => {
button.addEventListener('click', function() {
handleButtonClick(this, sendInstallRequest);
});
});
document.querySelectorAll('.update-btn').forEach(button => {
button.addEventListener('click', function() {
handleButtonClick(this, sendUpdateRequest);
});
});
document.querySelectorAll('.uninstall-btn').forEach(button => {
button.addEventListener('click', function() {
handleButtonClick(this, sendUninstallRequest);
});
});
const toggleMissingNodes = document.getElementById('toggle-missing-nodes');
const missingNodesList = document.getElementById('missing-nodes-list');
if (toggleMissingNodes && missingNodesList) {
toggleMissingNodes.addEventListener('click', function() {
missingNodesList.style.display = missingNodesList.style.display === 'none' ? 'block' : 'none';
});
}
}
function handleButtonClick(button, requestFunction) {
if (button.disabled) return;
const packageUrl = button.getAttribute('data-url');
const downloadModalData = button.getAttribute('data-download-modal');
const downloadModal = downloadModalData ? JSON.parse(downloadModalData) : {};
const statusMessage = button.closest('li').querySelector('.status-message');
const allButtons = document.querySelectorAll('.install-btn, .update-btn, .uninstall-btn');
allButtons.forEach(btn => {
btn.disabled = true;
btn.classList.add('disabled-button');
});
requestFunction(packageUrl, downloadModal, statusMessage)
.finally(() => {
allButtons.forEach(btn => {
btn.disabled = false;
btn.classList.remove('disabled-button');
});
});
}
function sendInstallRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Installing...';
return fetch('/flow/api/install-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl, downloadModal })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
statusMessageElement.textContent = `${data.message} Reset ComfyUI to apply changes.`;
} else if (data.status === 'already_installed') {
statusMessageElement.textContent = `${data.message} Reset ComfyUI to apply changes.`;
} else {
statusMessageElement.textContent = data.message || 'Installation failed.';
}
})
.catch(error => {
console.error('Installation error:', error);
statusMessageElement.textContent = 'Installation failed.';
});
}
function sendUpdateRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Updating...';
return fetch('/flow/api/update-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
statusMessageElement.textContent = `${data.message} \n Reset ComfyUI to apply changes.`;
} else if (data.status === 'not_installed') {
statusMessageElement.textContent = data.message;
} else {
statusMessageElement.textContent = data.message || 'Update failed.';
}
})
.catch(error => {
console.error('Update error:', error);
statusMessageElement.textContent = 'Update failed.';
});
}
function sendUninstallRequest(packageUrl, downloadModal, statusMessageElement) {
statusMessageElement.textContent = 'Uninstalling...';
return fetch('/flow/api/uninstall-package', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ packageUrl })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
statusMessageElement.textContent = `${data.message} Reset ComfyUI to apply changes.`;
} else if (data.status === 'not_installed') {
statusMessageElement.textContent = data.message;
} else {
statusMessageElement.textContent = data.message || 'Uninstallation failed.';
}
})
.catch(error => {
console.error('Uninstallation error:', error);
statusMessageElement.textContent = 'Uninstallation failed.';
});
}
export { checkAndShowMissingPackagesDialog };