image / assets /plugins /imageGenerator.js
hadadrjt's picture
image: Refac B/F. #2
e925412
//
// SPDX-FileCopyrightText: Hadad <[email protected]>
// SPDX-License-Identifier: Apache-2.0
//
(function () {
'use strict';
var ws = null;
var sessionId = '';
var reconnectTimer = null;
var isConnecting = false;
var currentImages = [];
if (document && document.body) {
sessionId = document.body.dataset.sessionId || '';
}
var connectWebSocket = function() {
if (isConnecting || (ws && ws.readyState === 1)) return;
isConnecting = true;
var protocol = window.location.protocol === 'https:'
? 'wss:' : 'ws:';
var wsUrl = protocol + '//' + window.location.host;
ws = new WebSocket(wsUrl);
ws.onopen = function() {
isConnecting = false;
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
ws.send(JSON.stringify({
type: 'register',
sessionId: sessionId
}));
};
ws.onmessage = function(event) {
try {
var data = JSON.parse(event.data);
if (!data.sessionId ||
data.sessionId !== sessionId) {
return;
}
handleWebSocketMessage(data);
} catch (e) {}
};
ws.onclose = function() {
isConnecting = false;
reconnectWebSocket();
};
ws.onerror = function() {
isConnecting = false;
if (ws) ws.close();
};
};
var reconnectWebSocket = function() {
if (reconnectTimer) return;
reconnectTimer = setTimeout(function() {
reconnectTimer = null;
connectWebSocket();
}, 1000);
};
var handleWebSocketMessage = function(data) {
if (!data || !data.type) return;
var handlers = {
'progressUpdate': function() {
updateProgressUI(data.progress);
},
'generationStarted': showGeneratingUI,
'generationComplete': function() {
handleGenerationComplete(data.images);
},
'generationError': function() {
handleGenerationError(data.error);
},
'generationCancelled': handleGenerationCancelled,
'imageDeleted': function() {
handleImageDeleted(data.images);
}
};
if (handlers[data.type]) {
handlers[data.type]();
}
};
var updateProgressUI = function(progress) {
var progressFill = document.querySelector(
'.progress-fill'
);
var progressText = document.querySelector(
'.progress-text'
);
if (progressFill) {
progressFill.style.width = progress + '%';
}
if (progressText) {
progressText.textContent =
Math.floor(progress) + '% Complete';
}
};
var toggleFormInputs = function(disabled) {
var form = document.getElementById('generateForm');
var inputs = form ? form.querySelectorAll(
'input, select, textarea'
) : [];
Array.prototype.forEach.call(inputs, function(input) {
input.disabled = disabled;
});
};
var showGeneratingUI = function() {
var outputSection = document.querySelector(
'.image-output-section'
);
toggleFormInputs(true);
if (outputSection) {
outputSection.classList.remove('has-images');
outputSection.innerHTML = [
'<div class="loading-container">',
'<div class="loading-spinner" ',
'style="margin: 0 auto 20px;"></div>',
'<p class="loading-text">',
'Generating your image...</p>',
'<div class="progress-bar">',
'<div class="progress-fill" ',
'style="width: 0%;"></div>',
'</div>',
'<p class="progress-text">0% Complete</p>',
'</div>'
].join('');
}
updateButtonsForGeneration(true);
};
var hideGeneratingUI = function() {
toggleFormInputs(false);
updateButtonsForGeneration(false);
if (window.validateInputs) {
window.validateInputs();
}
};
var resetToInitialState = function() {
hideGeneratingUI();
if (currentImages && currentImages.length > 0) {
displayImages(currentImages);
} else {
showPlaceholder();
}
};
var showPlaceholder = function() {
var outputSection = document.querySelector(
'.image-output-section'
);
if (!outputSection) return;
outputSection.classList.remove('has-images');
outputSection.innerHTML = [
'<svg class="placeholder-icon" ',
'width="80" height="80" ',
'viewBox="0 0 24 24" fill="none">',
'<path d="M21 3H3C2 3 1 4 1 5V19C1 20 2 21 ',
'3 21H21C22 21 23 20 23 19V5C23 4 22 3 21 3Z',
'M21 19H3V5H21V19Z" fill="currentColor"/>',
'<path d="M4.5 16.5L9 12L11.5 14.5L16 10L',
'19.5 13.5" stroke="currentColor" ',
'stroke-width="1.5" stroke-linecap="round"/>',
'<circle cx="8" cy="8.5" r="1.5" ',
'fill="currentColor"/>',
'</svg>',
'<p class="placeholder-text">',
'No images generated yet. ',
'Start creating amazing visuals!',
'</p>'
].join('');
};
var createButton = function(type, isGenerating) {
var icons = {
stop: '<rect x="4" y="4" width="16" height="16" ' +
'rx="3" fill="currentColor"/>',
play: '<path d="M3 20V4L22 12L3 20ZM5 17L16.85 ' +
'12L5 7V10.5L11 12L5 13.5V17Z" ' +
'fill="currentColor"/>'
};
if (isGenerating) {
return [
'<button type="button" ',
'onclick="cancelGeneration()" ',
'class="btn btn-danger">',
'<svg class="button-icon" viewBox="0 0 24 24" ',
'fill="none">',
icons.stop,
'</svg>',
'Stop Generation',
'</button>'
].join('');
}
return [
'<button type="submit" id="submitBtn" disabled ',
'class="btn btn-primary">',
'<svg class="button-icon" viewBox="0 0 24 24" ',
'fill="none">',
icons.play,
'</svg>',
'Generate Image',
'</button>'
].join('');
};
var updateButtonsForGeneration = function(isGenerating) {
var buttonsContainer = document.querySelector(
'.flex.justify-center.gap-4'
);
if (!buttonsContainer) return;
buttonsContainer.innerHTML = createButton(
isGenerating ? 'stop' : 'play',
isGenerating
);
};
var handleGenerationComplete = function(images) {
currentImages = images || [];
hideGeneratingUI();
displayImages(currentImages);
};
var handleGenerationError = function(error) {
resetToInitialState();
showErrorModal(error);
};
var handleGenerationCancelled = function() {
resetToInitialState();
};
var handleImageDeleted = function(images) {
currentImages = images || [];
displayImages(currentImages);
};
var createImageCard = function(image, index) {
var downloadIcon = [
'<path d="M12 16L7 11L8.4 9.55L11 12.15V4H13',
'V12.15L15.6 9.55L17 11L12 16Z" ',
'fill="currentColor"/>',
'<path d="M4 20C3.45 20 2.98 19.8 2.59 19.41',
'C2.2 19.02 2 18.55 2 18V15H4V18H20V15H22V18',
'C22 18.55 21.8 19.02 21.41 19.41C21.02 19.8 ',
'20.55 20 20 20H4Z" fill="currentColor"/>'
].join('');
var deleteIcon = [
'<path d="M18.3 5.71C17.91 5.32 17.28 5.32 ',
'16.89 5.71L12 10.59L7.11 5.7C6.72 5.31 6.09 ',
'5.31 5.7 5.7C5.31 6.09 5.31 6.72 5.7 7.11',
'L10.59 12L5.7 16.89C5.31 17.28 5.31 17.91 ',
'5.7 18.3C6.09 18.69 6.72 18.69 7.11 18.3L12 ',
'13.41L16.89 18.3C17.28 18.69 17.91 18.69 ',
'18.3 18.3C18.69 17.91 18.69 17.28 18.3 ',
'16.89L13.41 12L18.3 7.11C18.68 6.73 18.68 ',
'6.09 18.3 5.71Z" fill="currentColor"/>'
].join('');
return [
'<div class="image-card">',
'<img src="data:image/png;base64,',
image.base64,
'" alt="', image.prompt, '">',
'<div class="image-actions">',
'<a href="data:image/png;base64,',
image.base64,
'" download="generated-', image.id, '.png" ',
'class="action-btn">',
'<svg class="action-icon" viewBox="0 0 24 24" ',
'fill="none">',
downloadIcon,
'</svg>',
'</a>',
'<button type="button" onclick="deleteImage(',
index,
')" class="action-btn">',
'<svg class="action-icon" viewBox="0 0 24 24" ',
'fill="none">',
deleteIcon,
'</svg>',
'</button>',
'</div>',
'<div class="image-info">',
'<p class="image-prompt">', image.prompt, '</p>',
'<p class="image-meta">',
'<span class="image-model">',
image.model.toUpperCase(),
'</span> | ',
image.size,
'</p>',
'</div>',
'</div>'
].join('');
};
var displayImages = function(images) {
var outputSection = document.querySelector(
'.image-output-section'
);
if (!outputSection) return;
if (!images || images.length === 0) {
showPlaceholder();
} else {
outputSection.classList.add('has-images');
var html = ['<div class="image-grid">'];
images.forEach(function(image, index) {
html.push(createImageCard(image, index));
});
html.push('</div>');
outputSection.innerHTML = html.join('');
}
};
var showErrorModal = function(error) {
var existingModal = document.getElementById(
'errorModal'
);
if (existingModal) existingModal.remove();
var modal = document.createElement('div');
modal.id = 'errorModal';
modal.className = 'modal-overlay';
modal.innerHTML = [
'<div class="modal-content ',
'modal-error-content">',
'<div class="modal-inner">',
'<h3 class="modal-error-title">Error</h3>',
'<p class="modal-error-text">', error, '</p>',
'<button onclick="closeErrorModal()" ',
'class="btn btn-primary w-full">OK</button>',
'</div>',
'</div>'
].join('');
document.body.appendChild(modal);
};
window.deleteImage = function(index) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/', true);
xhr.setRequestHeader(
'Content-Type',
'application/json'
);
xhr.onload = function() {
try {
var response = JSON.parse(xhr.responseText);
if (!response.success && response.error) {
showErrorModal(response.error);
}
} catch (e) {}
};
xhr.send(JSON.stringify({
action: 'delete',
sessionId: sessionId,
imageIndex: index
}));
};
var initializeImages = function() {
var outputSection = document.querySelector(
'.image-output-section'
);
if (!outputSection ||
!outputSection.classList.contains('has-images')) {
return;
}
var imageCards = outputSection.querySelectorAll(
'.image-card'
);
if (!imageCards || imageCards.length === 0) return;
currentImages = [];
imageCards.forEach(function(card) {
var img = card.querySelector('img');
var prompt = card.querySelector('.image-prompt');
var model = card.querySelector('.image-model');
var meta = card.querySelector('.image-meta');
if (img && img.src && img.src.includes('base64,')) {
var base64 = img.src.split('base64,')[1];
var size = meta
? meta.textContent.split('|')[1]
: '';
currentImages.push({
id: 'existing-' + Math.random()
.toString(36).substring(2, 15),
base64: base64,
prompt: prompt ? prompt.textContent : '',
model: model
? model.textContent.toLowerCase()
: '',
size: size ? size.trim() : ''
});
}
});
};
connectWebSocket();
initializeImages();
})();