Madewithwebsim / 1PT5G752aPFlS0Uiy.html
allknowingroger's picture
Upload 18 files
54ba9fd verified
raw
history blame contribute delete
No virus
41.1 kB
<html><head><base href="https://treegen.ai"><title>Promptimg - AI Image Prompt Generator</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #0f1628;
color: #ffffff;
display: flex;
flex-direction: column;
min-height: 100vh;
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
flex: 1;
}
h1 {
color: #ffffff;
text-align: center;
font-size: 2rem;
}
#config {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-bottom: 20px;
background-color: #1e293b;
padding: 15px;
border-radius: 8px;
}
#config label {
margin-right: 15px;
font-size: 14px;
margin-bottom: 5px;
}
#config input[type="number"] {
width: 60px;
padding: 5px;
background-color: #2d3748;
border: 1px solid #4a5568;
color: #ffffff;
border-radius: 4px;
margin-bottom: 5px;
}
#config input[type="range"] {
width: 150px;
margin: 0 10px 5px 10px;
}
#prompt-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
overflow-x: auto;
}
.prompt-layer {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 10px;
position: relative;
width: 100%;
}
.prompt-group {
background-color: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.3);
border-radius: 10px;
padding: 15px;
margin-top: 30px;
position: relative;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: center;
width: 100%;
max-width: 600px;
}
.prompt-group::before {
content: '';
position: absolute;
top: -20px;
left: 50%;
width: 2px;
height: 20px;
background-color: rgba(59, 130, 246, 0.5);
}
.prompt-group-title {
position: absolute;
top: -25px;
left: 50%;
transform: translateX(-50%);
background-color: #0f1628;
padding: 0 10px;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
}
.prompt-group-title-hidden {
display: none;
}
.prompt-group-user-input {
position: absolute;
top: -25px;
left: 50%;
transform: translateX(-50%);
background-color: #0f1628;
padding: 0 10px;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
}
.prompt-card {
position: relative;
cursor: pointer;
flex-shrink: 0;
margin: 5px;
width: 100px;
height: 100px;
}
.prompt-card img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
transition: all 0.3s ease;
}
.prompt-card:hover img {
filter: brightness(70%);
}
.prompt-card p {
display: none;
}
#prompt-input {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #3b4b66;
border-radius: 4px;
background-color: #1e293b;
color: #ffffff;
box-sizing: border-box;
}
#generate-btn {
display: block;
width: 100%;
max-width: 250px;
margin: 0 auto 20px;
padding: 10px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
#generate-btn:hover {
background-color: #2563eb;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.8);
}
.modal-content {
background-color: #1e293b;
margin: 5% auto;
padding: 20px;
border: 1px solid #3b4b66;
width: 90%;
max-width: 800px;
border-radius: 10px;
color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.close {
color: #aaa;
position: absolute;
top: 10px;
right: 20px;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.modal-content img {
max-width: 100%;
max-height: 60vh;
object-fit: contain;
margin-bottom: 20px;
}
.action-buttons {
display: none;
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(59, 130, 246, 0.8);
border-radius: 20px;
padding: 5px 10px;
z-index: 10;
}
.action-button {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 16px;
padding: 5px;
margin: 0 5px;
}
.action-button:hover {
color: #f0f0f0;
}
#download-btn {
position: absolute;
top: 10px;
right: 60px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
}
#download-btn:hover {
background-color: #2563eb;
}
footer {
background-color: #1e293b;
color: #ffffff;
text-align: center;
padding: 10px;
font-size: 14px;
}
.nav-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 2rem;
color: #ffffff;
background-color: rgba(59, 130, 246, 0.5);
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.3s ease;
}
.nav-arrow:hover {
background-color: rgba(59, 130, 246, 0.8);
}
#prev-arrow {
left: 20px;
}
#next-arrow {
right: 20px;
}
#thumbnails {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
flex-wrap: wrap;
}
.thumbnail {
width: 60px;
height: 60px;
border-radius: 50%;
object-fit: cover;
cursor: pointer;
transition: all 0.3s ease;
}
.thumbnail:hover, .thumbnail.active {
border: 2px solid #3b82f6;
transform: scale(1.1);
}
.group-action-buttons {
display: none;
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(59, 130, 246, 0.8);
border-radius: 20px;
padding: 5px 10px;
z-index: 10;
}
.prompt-layer {
overflow: visible;
}
.prompt-group {
overflow: visible;
}
.add-node-btn {
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(59, 130, 246, 0.2);
color: white;
border: 2px dashed rgba(59, 130, 246, 0.5);
border-radius: 50%;
width: 100px;
height: 100px;
font-size: 40px;
cursor: pointer;
transition: all 0.3s ease;
}
.add-node-btn:hover {
background-color: rgba(59, 130, 246, 0.4);
}
.parent-image {
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 30px;
border-radius: 50%;
overflow: hidden;
border: 2px solid #3b82f6;
}
.parent-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.edit-prompt-modal {
display: none;
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
}
.edit-prompt-content {
background-color: #1e293b;
margin: 10% auto;
padding: 20px;
border: 1px solid #3b4b66;
width: 90%;
max-width: 600px;
border-radius: 10px;
}
.edit-prompt-textarea {
width: 100%;
height: 100px;
margin-bottom: 10px;
padding: 10px;
background-color: #2d3748;
border: 1px solid #4a5568;
color: #ffffff;
border-radius: 4px;
resize: vertical;
box-sizing: border-box;
}
.edit-prompt-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.edit-prompt-btn {
padding: 5px 10px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.edit-prompt-btn:hover {
background-color: #2563eb;
}
.highlighted-prompt {
margin-top: 10px;
font-size: 14px;
line-height: 1.4;
}
.highlighted-prompt span {
padding: 2px 4px;
border-radius: 3px;
}
.loading-message {
font-size: 18px;
color: #ffffff;
text-align: center;
margin: 20px 0;
}
@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}
.loading-message {
animation: pulse 1.5s infinite;
}
.image-details {
margin-top: 20px;
text-align: left;
width: 100%;
}
.image-details p {
margin: 5px 0;
}
.image-details .parent-prompt,
.image-details .user-input {
font-size: 0.9em;
color: #a0aec0;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-icon {
animation: spin 1s linear infinite;
}
.group-placeholder {
background-color: rgba(59, 130, 246, 0.1);
border: 1px dashed rgba(59, 130, 246, 0.3);
border-radius: 10px;
padding: 15px;
margin-top: 30px;
text-align: center;
min-width: 200px;
min-height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
z-index: 10;
}
.sidebar {
position: fixed;
top: 0;
left: -300px;
width: 100%;
max-width: 300px;
height: 100%;
background-color: #1e293b;
transition: left 0.3s ease-in-out;
z-index: 1000;
overflow-y: auto;
}
.sidebar.open {
left: 0;
}
.sidebar-content {
padding: 20px;
color: #ffffff;
}
.sidebar h2 {
margin-top: 0;
}
.sidebar ul {
padding-left: 20px;
}
.sidebar li {
margin-bottom: 10px;
}
.toggle-sidebar {
position: fixed;
top: 20px;
left: 20px;
z-index: 1001;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
padding: 10px;
cursor: pointer;
}
.modal-image-container {
position: relative;
max-width: 100%;
max-height: 60vh;
""
margin-bottom: 20px;
}
.modal-image-container img {
max-width: 100%;
max-height: 60vh;
object-fit: contain;
width: auto;
}
.modal-prompt-overlay {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 10px;
box-sizing: border-box;
opacity: 0;
transition: opacity 0.3s ease;
}
.modal-image-container:hover .modal-prompt-overlay {
opacity: 1;
}
.circular-button {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #3b82f6;
color: white;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 0 5px;
transition: background-color 0.3s ease;
}
.circular-button:hover {
background-color: #2563eb;
}
.modal-buttons {
display: flex;
justify-content: center;
margin-top: 10px;
}
.action-toggle {
display: none;
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(59, 130, 246, 0.8);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
font-size: 16px;
cursor: pointer;
z-index: 9;
}
.group-action-toggle {
display: none;
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(59, 130, 246, 0.8);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
font-size: 16px;
cursor: pointer;
z-index: 9;
}
@media (min-width: 769px) {
.prompt-card:hover .action-buttons {
display: flex;
}
.prompt-group:hover .group-action-buttons {
display: flex;
}
.action-toggle, .group-action-toggle {
display: none !important;
}
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
h1 {
font-size: 1.5rem;
}
#config {
flex-direction: column;
align-items: flex-start;
}
#config label, #config input {
margin-bottom: 10px;
}
.prompt-group {
padding: 10px;
}
.nav-arrow {
font-size: 1.5rem;
width: 40px;
height: 40px;
}
#prev-arrow {
left: 10px;
}
#next-arrow {
right: 10px;
}
.thumbnail {
width: 50px;
height: 50px;
}
.action-buttons, .group-action-buttons {
display: none;
}
.action-toggle, .group-action-toggle {
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<button class="toggle-sidebar" id="toggleSidebar">
<i class="fas fa-bars"></i>
</button>
<div class="sidebar" id="sidebar">
<div class="sidebar-content">
<h2><i class="fas fa-lightbulb"></i> How to Use Promptimg</h2>
<ol>
<li>Enter an initial concept in the input field.</li>
<li>Click "Generate image prompts" to create a group of prompts.</li>
<li>Explore the generated images by clicking on them.</li>
<li>Use the action buttons to:
<ul>
<li><i class="fas fa-redo"></i> Regenerate an image or entire group of prompts</li>
<li><i class="fas fa-edit"></i> Edit a prompt</li>
<li><i class="fas fa-trash"></i> Delete a prompt</li>
<li><i class="fas fa-sitemap"></i> Create child nodes (bifurcate)</li>
</ul>
</li>
<li>Adjust the number of prompts and node size using the controls at the top.</li>
<li>Create new groups by bifurcating or adding nodes to existing groups.</li>
</ol>
<div class="sidebar-footer">
<p><i class="fas fa-code"></i> Promptimg v0.1.0-release</p>
<p><i class="fas fa-heart"></i> Made with love by @eechever</p>
</div>
</div>
</div>
<div class="container">
<h1>Promptimg - Find your dream prompts &amp; images</h1>
<div id="config">
<label for="num-prompts">Number of prompts:</label>
<input type="number" id="num-prompts" min="1" max="10" value="3">
<label for="node-size">Node size:</label>
<input type="range" id="node-size" min="20" max="200" value="100">
<span id="node-size-value">100px</span>
</div>
<input type="text" id="prompt-input" placeholder="Enter your initial concept">
<button id="generate-btn">Generate image prompts</button>
<div id="prompt-container"></div>
</div>
<div id="imageModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<button id="prev-arrow" class="nav-arrow"><i class="fas fa-chevron-left"></i></button>
<button id="next-arrow" class="nav-arrow"><i class="fas fa-chevron-right"></i></button>
<div class="modal-image-container">
<img id="modalImage" src="" alt="Full size image">
<div class="modal-prompt-overlay">
<p id="modalDescription"></p>
</div>
</div>
<div class="modal-buttons">
<button id="copyPromptBtn" class="circular-button" title="Copy Prompt">
<i class="fas fa-copy"></i>
</button>
<button id="downloadImageBtn" class="circular-button" title="Download Image">
<i class="fas fa-download"></i>
</button>
</div>
<div class="image-details">
<p id="userInput" class="user-input"></p>
</div>
<div id="thumbnails"></div>
</div>
</div>
<div id="editPromptModal" class="edit-prompt-modal">
<div class="edit-prompt-content">
<textarea id="editPromptTextarea" class="edit-prompt-textarea"></textarea>
<div class="edit-prompt-buttons">
<button id="cancelEditPrompt" class="edit-prompt-btn">Cancel</button>
<button id="saveEditPrompt" class="edit-prompt-btn">Save</button>
</div>
</div>
</div>
<footer>
<p>Made with ❤️ by <a href="https://www.linkedin.com/in/eechev/" target="_blank" style="color: white; text-decoration: none; transition: opacity 0.3s ease;" onmouseover="this.style.opacity='0.7'" onmouseout="this.style.opacity='1'">@eechever</a> and WebSim</p>
<a href="https://buymeacoffee.com/eechever" target="_blank" style="display: inline-block; background-color: #FFDD00; color: #000000; padding: 8px 16px; text-decoration: none; border-radius: 5px; font-weight: bold; margin-top: 10px;">
<i class="fas fa-coffee" style="margin-right: 5px;"></i> Buy me a coffee
</a>
</footer>
<a id="hiddenDownloadLink" style="display: none;"></a>
<script>
const promptContainer = document.getElementById('prompt-container');
const promptInput = document.getElementById('prompt-input');
const generateBtn = document.getElementById('generate-btn');
const numPromptsInput = document.getElementById('num-prompts');
const nodeSizeInput = document.getElementById('node-size');
const nodeSize = document.getElementById('node-size-value');
const modal = document.getElementById('imageModal');
const modalImage = document.getElementById('modalImage');
const modalDescription = document.getElementById('modalDescription');
const userInput = document.getElementById('userInput');
const closeModal = document.getElementsByClassName('close')[0];
const prevArrow = document.getElementById('prev-arrow');
const nextArrow = document.getElementById('next-arrow');
const thumbnailsContainer = document.getElementById('thumbnails');
const editPromptModal = document.getElementById('editPromptModal');
const editPromptTextarea = document.getElementById('editPromptTextarea');
const cancelEditPrompt = document.getElementById('cancelEditPrompt');
const saveEditPrompt = document.getElementById('saveEditPrompt');
const sidebar = document.getElementById('sidebar');
const toggleSidebar = document.getElementById('toggleSidebar');
const copyPromptBtn = document.getElementById('copyPromptBtn');
const downloadImageBtn = document.getElementById('downloadImageBtn');
const hiddenDownloadLink = document.getElementById('hiddenDownloadLink');
let currentGroupImages = [];
let currentImageIndex = 0;
let currentEditingCard = null;
let currentOpenActionButtons = null;
generateBtn.addEventListener('click', () => {
const initialConcept = promptInput.value;
if (initialConcept) {
setButtonLoading(generateBtn);
generatePrompts(initialConcept);
}
});
nodeSizeInput.addEventListener('input', () => {
nodeSize.textContent = `${nodeSizeInput.value}px`;
updateNodeSize();
});
closeModal.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
if (event.target == editPromptModal) {
editPromptModal.style.display = "none";
}
if (window.innerWidth <= 768 && currentOpenActionButtons && !event.target.closest('.action-buttons') && !event.target.closest('.action-toggle') && !event.target.closest('.group-action-buttons') && !event.target.closest('.group-action-toggle')) {
currentOpenActionButtons.style.display = 'none';
currentOpenActionButtons = null;
}
}
prevArrow.addEventListener('click', () => navigateImages(-1));
nextArrow.addEventListener('click', () => navigateImages(1));
document.addEventListener('keydown', (e) => {
if (modal.style.display === "block") {
if (e.key === "ArrowLeft") navigateImages(-1);
if (e.key === "ArrowRight") navigateImages(1);
}
});
cancelEditPrompt.addEventListener('click', () => {
editPromptModal.style.display = "none";
});
saveEditPrompt.addEventListener('click', () => {
if (currentEditingCard) {
const newPrompt = editPromptTextarea.value;
const promptData = JSON.parse(currentEditingCard.querySelector('p').textContent);
promptData.text = newPrompt;
currentEditingCard.querySelector('p').textContent = JSON.stringify(promptData);
regenerateImage(currentEditingCard);
editPromptModal.style.display = "none";
}
});
toggleSidebar.addEventListener('click', () => {
sidebar.classList.toggle('open');
});
copyPromptBtn.addEventListener('click', () => {
const promptText = modalDescription.textContent;
navigator.clipboard.writeText(promptText).then(() => {
alert('Prompt copied to clipboard!');
}).catch(err => {
console.error('Failed to copy text: ', err);
});
});
downloadImageBtn.addEventListener('click', function(e) {
e.preventDefault();
hiddenDownloadLink.click();
});
function updateNodeSize() {
const size = nodeSizeInput.value;
const promptCards = document.querySelectorAll('.prompt-card, .add-node-btn');
promptCards.forEach(card => {
card.style.width = `${size}px`;
card.style.height = `${size}px`;
});
}
async function generatePrompts(initialConcept) {
const placeholder = createGroupPlaceholder("Generating prompts...");
const layer = getOrCreateRootLayer();
layer.appendChild(placeholder);
const numPrompts = parseInt(numPromptsInput.value);
const complexPrompts = await generateComplexPrompts(initialConcept, numPrompts);
placeholder.innerHTML = "Generating images...";
const group = await createPromptGroup(complexPrompts, initialConcept, initialConcept);
layer.replaceChild(group, placeholder);
updateNodeSize();
resetButtonLoading(generateBtn);
}
function getOrCreateRootLayer() {
let rootLayer = promptContainer.querySelector('.prompt-layer');
if (!rootLayer) {
rootLayer = createPromptLayer();
promptContainer.appendChild(rootLayer);
}
return rootLayer;
}
function createPromptLayer() {
const layer = document.createElement('div');
layer.className = 'prompt-layer';
return layer;
}
function createGroupPlaceholder(message) {
const placeholder = document.createElement('div');
placeholder.className = 'group-placeholder';
placeholder.innerHTML = `
<i class="fas fa-spinner fa-spin fa-2x"></i>
<p>${message}</p>
`;
return placeholder;
}
async function createPromptGroup(prompts, parentPrompt, userInputText, parentImage = null) {
const group = document.createElement('div');
group.className = 'prompt-group';
const hiddenTitle = document.createElement('div');
hiddenTitle.className = 'prompt-group-title prompt-group-title-hidden';
hiddenTitle.textContent = parentPrompt;
group.appendChild(hiddenTitle);
const userInputTitle = document.createElement('div');
userInputTitle.className = 'prompt-group-user-input';
userInputTitle.textContent = userInputText;
group.appendChild(userInputTitle);
if (parentImage) {
const parentImageContainer = document.createElement('div');
parentImageContainer.className = 'parent-image';
const img = document.createElement('img');
img.src = parentImage;
img.alt = 'Parent Image';
parentImageContainer.appendChild(img);
group.appendChild(parentImageContainer);
}
const groupActionButtons = document.createElement('div');
groupActionButtons.className = 'group-action-buttons';
groupActionButtons.innerHTML = `
<button class="action-button" title="Regenerate group"><i class="fas fa-redo"></i></button>
<button class="action-button" title="Edit group prompt"><i class="fas fa-edit"></i></button>
<button class="action-button" title="Delete group"><i class="fas fa-trash"></i></button>
`;
group.appendChild(groupActionButtons);
const groupActionToggle = document.createElement('button');
groupActionToggle.className = 'group-action-toggle';
groupActionToggle.innerHTML = '<i class="fas fa-ellipsis-v"></i>';
group.appendChild(groupActionToggle);
const regenerateGroupBtn = groupActionButtons.querySelector('.action-button:nth-child(1)');
const editGroupBtn = groupActionButtons.querySelector('.action-button:nth-child(2)');
const deleteGroupBtn = groupActionButtons.querySelector('.action-button:nth-child(3)');
regenerateGroupBtn.addEventListener('click', (e) => {
e.stopPropagation();
regenerateGroup(group);
});
editGroupBtn.addEventListener('click', (e) => {
e.stopPropagation();
editGroupPrompt(group);
});
deleteGroupBtn.addEventListener('click', (e) => {
e.stopPropagation();
deleteGroup(group);
});
groupActionToggle.addEventListener('click', (e) => {
e.stopPropagation();
toggleGroupActionButtons(group);
});
for (const prompt of prompts) {
const imageUrl = await generateImage(prompt.text);
const promptCard = createPromptCard(prompt, imageUrl, parentPrompt, userInputText);
group.appendChild(promptCard);
}
const addNodeBtn = document.createElement('div');
addNodeBtn.className = 'add-node-btn prompt-card';
addNodeBtn.innerHTML = '<i class="fas fa-plus"></i>';
addNodeBtn.addEventListener('click', (e) => {
e.stopPropagation();
addNewNode(group);
});
group.appendChild(addNodeBtn);
return group;
}
function createPromptCard(prompt, imageUrl, parentPrompt, userInputText) {
const card = document.createElement('div');
card.className = 'prompt-card';
card.innerHTML = `
<img src="${imageUrl}" alt="Generated image for ${prompt.text}">
<p>${JSON.stringify({...prompt, parentPrompt, userInputText})}</p>
<div class="action-buttons">
<button class="action-button" title="Regenerate image"><i class="fas fa-redo"></i></button>
<button class="action-button" title="Edit prompt"><i class="fas fa-edit"></i></button>
<button class="action-button" title="Delete prompt"><i class="fas fa-trash"></i></button>
<button class="action-button" title="Bifurcate/Create nodes"><i class="fas fa-sitemap"></i></button>
</div>
<button class="action-toggle"><i class="fas fa-ellipsis-v"></i></button>
`;
const regenerateBtn = card.querySelector('.action-button:nth-child(1)');
const editBtn = card.querySelector('.action-button:nth-child(2)');
const deleteBtn = card.querySelector('.action-button:nth-child(3)');
const bifurcateBtn = card.querySelector('.action-button:nth-child(4)');
const actionToggle = card.querySelector('.action-toggle');
const actionButtons = card.querySelector('.action-buttons');
regenerateBtn.addEventListener('click', (e) => {
e.stopPropagation();
regenerateImage(card);
});
editBtn.addEventListener('click', (e) => {
e.stopPropagation();
editPromptWithModal(card);
});
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
deletePrompt(card);
});
bifurcateBtn.addEventListener('click', (e) => {
e.stopPropagation();
createChildNodesWithPrompt(card);
});
actionToggle.addEventListener('click', (e) => {
e.stopPropagation();
toggleActionButtons(card);
});
card.addEventListener('click', () => showFullImage(card));
return card;
}
function toggleActionButtons(card) {
const actionButtons = card.querySelector('.action-buttons');
if (window.innerWidth <= 768) {
if (currentOpenActionButtons && currentOpenActionButtons !== actionButtons) {
currentOpenActionButtons.style.display = 'none';
}
actionButtons.style.display = actionButtons.style.display === 'flex' ? 'none' : 'flex';
currentOpenActionButtons = actionButtons.style.display === 'flex' ? actionButtons : null;
}
}
function toggleGroupActionButtons(group) {
const groupActionButtons = group.querySelector('.group-action-buttons');
if (window.innerWidth <= 768) {
if (currentOpenActionButtons && currentOpenActionButtons !== groupActionButtons) {
currentOpenActionButtons.style.display = 'none';
}
groupActionButtons.style.display = groupActionButtons.style.display === 'flex' ? 'none' : 'flex';
currentOpenActionButtons = groupActionButtons.style.display === 'flex' ? groupActionButtons : null;
}
}
function showFullImage(card) {
const group = card.closest('.prompt-group');
currentGroupImages = Array.from(group.querySelectorAll('.prompt-card')).filter(card => !card.classList.contains('add-node-btn'));
currentImageIndex = currentGroupImages.indexOf(card);
updateModalImage();
modal.style.display = "block";
updateThumbnails();
}
function updateModalImage() {
const currentCard = currentGroupImages[currentImageIndex];
const imgSrc = currentCard.querySelector('img').src;
const promptData = JSON.parse(currentCard.querySelector('p').textContent);
modalImage.src = imgSrc;
modalDescription.textContent = promptData.text;
userInput.textContent = `User Input: ${promptData.userInputText}`;
// Update hidden download link
const fileName = `${promptData.text.slice(0, 30).replace(/[^a-z0-9]/gi, '_').toLowerCase()}.jpg`;
hiddenDownloadLink.href = imgSrc;
hiddenDownloadLink.download = fileName;
}
function updateThumbnails() {
thumbnailsContainer.innerHTML = '';
currentGroupImages.forEach((card, index) => {
const thumbnail = document.createElement('img');
thumbnail.src = card.querySelector('img').src;
thumbnail.alt = `Thumbnail ${index + 1}`;
thumbnail.classList.add('thumbnail');
if (index === currentImageIndex) thumbnail.classList.add('active');
thumbnail.addEventListener('click', () => {
currentImageIndex = index;
updateModalImage();
updateThumbnails();
});
thumbnailsContainer.appendChild(thumbnail);
});
}
function navigateImages(direction) {
currentImageIndex = (currentImageIndex + direction + currentGroupImages.length) % currentGroupImages.length;
updateModalImage();
updateThumbnails();
}
async function regenerateImage(card) {
const promptData = JSON.parse(card.querySelector('p').textContent);
setCardLoading(card);
const newImageUrl = await generateImage(promptData.text);
const oldImg = card.querySelector('img');
const newImg = document.createElement('img');
newImg.src = newImageUrl;
newImg.alt = `Generated image for ${promptData.text}`;
oldImg.parentNode.replaceChild(newImg, oldImg);
resetCardLoading(card);
}
function editPromptWithModal(card) {
currentEditingCard = card;
const currentPrompt = JSON.parse(card.querySelector('p').textContent);
editPromptTextarea.value = currentPrompt.text;
editPromptModal.style.display = "block";
}
function deletePrompt(card) {
if (confirm("Are you sure you want to delete this prompt?")) {
const parentGroup = card.closest('.prompt-group');
card.remove();
if (parentGroup.querySelectorAll('.prompt-card').length === 1) { // Only the add-node-btn left
deleteGroup(parentGroup);
}
}
}
async function createChildNodesWithPrompt(card) {
const promptData = JSON.parse(card.querySelector('p').textContent);
const userPrompt = prompt("Enter an optional prompt to add to the request (or leave blank):");
if (userPrompt === null) {
return; // User canceled the operation
}
setButtonLoading(card.querySelector('.action-button:nth-child(4)'));
const placeholder = createGroupPlaceholder("Generating prompts...");
let nextLayer = card.closest('.prompt-layer').nextElementSibling;
if (!nextLayer || !nextLayer.classList.contains('prompt-layer')) {
nextLayer = createPromptLayer();
promptContainer.insertBefore(nextLayer, card.closest('.prompt-layer').nextElementSibling);
}
nextLayer.appendChild(placeholder);
const childNodes = await generateComplexPrompts(promptData.text, 3, userPrompt);
placeholder.innerHTML = "Generating images...";
const parentImage = card.querySelector('img').src;
const newGroup = await createPromptGroup(childNodes, promptData.text, userPrompt || promptData.text, parentImage);
nextLayer.replaceChild(newGroup, placeholder);
updateNodeSize();
resetButtonLoading(card.querySelector('.action-button:nth-child(4)'));
}
async function regenerateGroup(group) {
const groupTitle = group.querySelector('.prompt-group-title');
const basePrompt = groupTitle.textContent;
const numPrompts = group.querySelectorAll('.prompt-card:not(.add-node-btn)').length;
const userInputTitle = group.querySelector('.prompt-group-user-input');
const userInputText = userInputTitle.textContent;
setButtonLoading(group.querySelector('.action-button:nth-child(1)'));
const placeholder = createGroupPlaceholder("Generating prompts...");
group.innerHTML = '';
group.appendChild(placeholder);
const newPrompts = await generateComplexPrompts(basePrompt, numPrompts, userInputText);
placeholder.innerHTML = "Generating images...";
group.innerHTML = '';
group.appendChild(groupTitle);
group.appendChild(userInputTitle);
for (const prompt of newPrompts) {
const newImageUrl = await generateImage(prompt.text);
const promptCard = createPromptCard(prompt, newImageUrl, basePrompt, userInputText);
group.appendChild(promptCard);
}
const addNodeBtn = document.createElement('div');
addNodeBtn.className = 'add-node-btn prompt-card';
addNodeBtn.innerHTML = '<i class="fas fa-plus"></i>';
addNodeBtn.addEventListener('click', (e) => {
e.stopPropagation();
addNewNode(group);
});
group.appendChild(addNodeBtn);
updateNodeSize();
resetButtonLoading(group.querySelector('.action-button:nth-child(1)'));
}
async function editGroupPrompt(group) {
const groupTitle = group.querySelector('.prompt-group-title');
const currentPrompt = groupTitle.textContent;
const userInputTitle = group.querySelector('.prompt-group-user-input');
const userIndication = prompt("Enter a new prompt for this group:", currentPrompt);
if (userIndication !== null && userIndication !== "") {
groupTitle.textContent = currentPrompt; // Keep the original parent prompt
userInputTitle.textContent = userIndication; // Update user input
await regenerateGroup(group);
}
}
function deleteGroup(group) {
if (confirm("Are you sure you want to delete this entire group?")) {
const parentLayer = group.closest('.prompt-layer');
group.remove();
if (parentLayer.querySelectorAll('.prompt-group').length === 0) {
parentLayer.remove();
}
}
}
async function addNewNode(group) {
const groupTitle = group.querySelector('.prompt-group-title');
const basePrompt = groupTitle.textContent;
const userInputTitle = group.querySelector('.prompt-group-user-input');
const userInputText = userInputTitle.textContent;
setButtonLoading(group.querySelector('.add-node-btn'));
const newPrompt = await generateComplexPrompts(basePrompt, 1, userInputText);
const newImageUrl = await generateImage(newPrompt[0].text);
const promptCard = createPromptCard(newPrompt[0], newImageUrl, basePrompt, userInputText);
group.insertBefore(promptCard, group.querySelector('.add-node-btn'));
updateNodeSize();
resetButtonLoading(group.querySelector('.add-node-btn'));
}
async function generateComplexPrompts(basePrompt, count, userPrompt = "") {
const response = await fetch('https://websim.ai/api/web', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: `Generate ${count} unique and creative image prompts based on this concept: "${basePrompt}". ${userPrompt ? `Include this additional context: "${userPrompt}".` : ''} Each prompt should be detailed and vivid, suitable for AI image generation. Respond in the following JSON format:
{
"prompts": [
{
"text": "Full prompt text"
},
...
]
}`,
}),
});
const data = await response.json();
return data.prompts;
}
async function generateImage(prompt) {
// Use a placeholder loading image
const placeholderUrl = 'https://via.placeholder.com/400x400.png?text=Loading...';
// Simulate image generation delay
await new Promise(resolve => setTimeout(resolve, 1000));
// For this example, we'll still use a placeholder image service
// In a real application, you would integrate with an actual AI image generation API
return `https://picsum.photos/400?random=${Math.random()}`;
}
function setButtonLoading(button) {
const originalContent = button.innerHTML;
button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
button.dataset.originalContent = originalContent;
}
function resetButtonLoading(button) {
const originalContent = button.dataset.originalContent;
if (originalContent) {
button.innerHTML = originalContent;
button.disabled = false;
delete button.dataset.originalContent;
}
}
function setCardLoading(card) {
const loadingOverlay = document.createElement('div');
loadingOverlay.className = 'loading-overlay';
loadingOverlay.innerHTML = '<i class="fas fa-spinner fa-spin fa-2x"></i>';
card.appendChild(loadingOverlay);
}
function resetCardLoading(card) {
const loadingOverlay = card.querySelector('.loading-overlay');
if (loadingOverlay) {
""
loadingOverlay.remove();
}
}
// Add this code to restore default link behavior after the page is loaded
document.addEventListener('DOMContentLoaded', function() {
const restoreDefaultLinkBehavior = function(element) {
const links = element.getElementsByTagName('a');
for (let i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(event) {
event.stopPropagation();
});
}
};
// Restore default behavior for existing links
restoreDefaultLinkBehavior(document);
// Use a MutationObserver to handle dynamically added content
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) { // ELEMENT_NODE
restoreDefaultLinkBehavior(node);
}
});
}
});
});
// Configure the observer to watch for changes in the entire document
const config = { childList: true, subtree: true };
observer.observe(document.body, config);
});
// Modify the download functionality
downloadImageBtn.addEventListener('click', function(e) {
e.preventDefault();
const imgSrc = modalImage.src;
const promptData = JSON.parse(currentGroupImages[currentImageIndex].querySelector('p').textContent);
const fileName = `${promptData.text.slice(0, 30).replace(/[^a-z0-9]/gi, '_').toLowerCase()}.jpg`;
fetch(imgSrc)
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch(() => alert('An error occurred while downloading the image.'));
});
</script>
</body></html>