DreamCanvas setup : to be reviewed
This commit is contained in:
97
On host/DreamCanvas/ui/index.html
Normal file
97
On host/DreamCanvas/ui/index.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DreamCanvas</title>
|
||||
<!-- Using Bootstrap's Cyborg theme -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/cyborg/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1 class="mb-4 text-left text-md-left text-center" style="font-size: 2rem;">DreamCanvas</h1>
|
||||
|
||||
<div class="row">
|
||||
<!-- Form Section -->
|
||||
<div class="col-md-6">
|
||||
<!-- Notification Banner for LLM Success -->
|
||||
<div id="notification" class="alert alert-success d-none" role="alert">
|
||||
LLM's creative prompt has been applied!
|
||||
</div>
|
||||
|
||||
<!-- Image Generation Form -->
|
||||
<form id="imageForm">
|
||||
<div class="mb-3">
|
||||
<label for="positivePrompt" class="form-label">Positive Prompt</label>
|
||||
<input type="text" class="form-control" id="positivePrompt" placeholder="Describe what you want">
|
||||
<small class="form-text text-muted">Examples: 4k, highly detailed, hyperrealistic</small>
|
||||
<div class="keyword-suggestions" id="positiveKeywords"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="negativePrompt" class="form-label">Negative Prompt</label>
|
||||
<input type="text" class="form-control" id="negativePrompt" placeholder="Describe what to avoid">
|
||||
<small class="form-text text-muted">Examples: blurry, watermark</small>
|
||||
<div class="keyword-suggestions" id="negativeKeywords"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="llmResponse" class="form-label">Creative Prompt from LLM</label>
|
||||
<textarea class="form-control" id="llmResponse" rows="3" readonly></textarea>
|
||||
<button type="button" id="askLLMButton" class="btn btn-info mt-3">
|
||||
<span id="askLLMText">Ask LLM for Creative Idea</span>
|
||||
<span id="askLLMSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" id="useLLMResponseButton" class="btn btn-success mt-3 d-none">Use LLM's Creative Prompt</button>
|
||||
</div>
|
||||
|
||||
<div id="quickPromptsContainer" class="mb-3"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="steps" class="form-label">Steps</label>
|
||||
<input type="number" class="form-control" id="steps" value="25" min="1">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="width" class="form-label">Width</label>
|
||||
<input type="number" class="form-control" id="width" value="512">
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="height" class="form-label">Height</label>
|
||||
<input type="number" class="form-control" id="height" value="512">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="generateBtn" class="btn btn-primary w-100 mb-3">
|
||||
<span id="buttonText">Generate Image</span>
|
||||
<span id="spinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
<span id="elapsedTime" class="d-none">(00:00)</span>
|
||||
</button>
|
||||
<button type="button" id="resetBtn" class="btn btn-secondary w-100">Reset</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Image Display Section -->
|
||||
<div class="col-md-6">
|
||||
<div id="imageResult" class="sticky-top mt-4">
|
||||
<img id="generatedImage" src="" alt="Generated Image" class="img-fluid d-none">
|
||||
<div id="imageNavigation" class="mt-3 d-none text-center">
|
||||
<button type="button" id="prevImage" class="btn btn-outline-light me-2">Previous</button>
|
||||
<button type="button" id="nextImage" class="btn btn-outline-light">Next</button>
|
||||
</div>
|
||||
<div id="promptDisplay" class="mt-3 d-none">
|
||||
<h5>Positive Prompts:</h5>
|
||||
<p id="positivePromptDisplay" class="text-success"></p>
|
||||
<h5>Negative Prompts:</h5>
|
||||
<p id="negativePromptDisplay" class="text-danger"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
316
On host/DreamCanvas/ui/script.js
Normal file
316
On host/DreamCanvas/ui/script.js
Normal file
@@ -0,0 +1,316 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const form = document.getElementById("imageForm");
|
||||
const generatedImage = document.getElementById("generatedImage");
|
||||
const generateBtn = document.getElementById("generateBtn");
|
||||
const spinner = document.getElementById("spinner");
|
||||
const buttonText = document.getElementById("buttonText");
|
||||
const elapsedTime = document.getElementById("elapsedTime");
|
||||
const positivePromptDisplay = document.getElementById("positivePromptDisplay");
|
||||
const negativePromptDisplay = document.getElementById("negativePromptDisplay");
|
||||
const promptDisplay = document.getElementById("promptDisplay");
|
||||
const quickPromptsContainer = document.getElementById("quickPromptsContainer");
|
||||
const positiveKeywordsContainer = document.getElementById("positiveKeywords");
|
||||
const negativeKeywordsContainer = document.getElementById("negativeKeywords");
|
||||
const askLLMButton = document.getElementById("askLLMButton");
|
||||
const askLLMSpinner = document.getElementById("askLLMSpinner");
|
||||
const askLLMText = document.getElementById("askLLMText");
|
||||
const llmResponseTextarea = document.getElementById("llmResponse");
|
||||
const useLLMResponseButton = document.getElementById("useLLMResponseButton");
|
||||
const notification = document.getElementById("notification");
|
||||
const resetBtn = document.getElementById("resetBtn");
|
||||
const imageNavigation = document.getElementById("imageNavigation");
|
||||
const prevImageBtn = document.getElementById("prevImage");
|
||||
const nextImageBtn = document.getElementById("nextImage");
|
||||
|
||||
let timerInterval;
|
||||
let startTime;
|
||||
let imageHistory = []; // Cache to store generated images
|
||||
let currentImageIndex = -1; // Current index in image history
|
||||
|
||||
// Function to start the timer
|
||||
function startTimer() {
|
||||
startTime = new Date().getTime(); // Reset the start time
|
||||
elapsedTime.innerText = "(00:00)"; // Reset display time
|
||||
elapsedTime.classList.remove("d-none");
|
||||
timerInterval = setInterval(function () {
|
||||
const now = new Date().getTime();
|
||||
const distance = now - startTime;
|
||||
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||
elapsedTime.innerText = `(${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')})`;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Function to stop the timer
|
||||
function stopTimer() {
|
||||
clearInterval(timerInterval);
|
||||
elapsedTime.classList.add("d-none");
|
||||
}
|
||||
|
||||
// Function to display the notification for LLM usage
|
||||
function showNotification(message, type = 'success') {
|
||||
notification.innerText = message;
|
||||
notification.classList.remove("d-none", "alert-success", "alert-danger");
|
||||
notification.classList.add(`alert-${type}`);
|
||||
|
||||
// Hide the notification after 3 seconds
|
||||
setTimeout(() => {
|
||||
notification.classList.add("d-none");
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Automatically scroll to the image result when it is generated
|
||||
function scrollToImage() {
|
||||
generatedImage.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
|
||||
// Function to reset the form
|
||||
resetBtn.addEventListener("click", function () {
|
||||
document.getElementById("positivePrompt").value = "";
|
||||
document.getElementById("negativePrompt").value = "";
|
||||
llmResponseTextarea.value = "";
|
||||
generatedImage.classList.add("d-none");
|
||||
promptDisplay.classList.add("d-none");
|
||||
useLLMResponseButton.classList.add("d-none");
|
||||
spinner.classList.add("d-none");
|
||||
buttonText.innerText = "Generate Image";
|
||||
imageHistory = [];
|
||||
currentImageIndex = -1;
|
||||
imageNavigation.classList.add("d-none");
|
||||
showNotification("The UI has been reset.");
|
||||
});
|
||||
|
||||
// Function to dynamically load quick prompts from the server
|
||||
function loadQuickPrompts() {
|
||||
fetch("/quick_prompts/")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
quickPromptsContainer.innerHTML = ''; // Clear any existing quick prompts
|
||||
positiveKeywordsContainer.innerHTML = ''; // Clear positive prompt buttons
|
||||
negativeKeywordsContainer.innerHTML = ''; // Clear negative prompt buttons
|
||||
|
||||
// Load Positive Quick Prompts
|
||||
if (data["Positive Quick Prompts"]) {
|
||||
data["Positive Quick Prompts"].forEach(prompt => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.classList.add('btn', 'btn-secondary', 'btn-sm', 'me-2', 'mb-2');
|
||||
button.innerText = prompt.label;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
addPositiveKeyword(button, prompt.value);
|
||||
});
|
||||
|
||||
positiveKeywordsContainer.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
// Load Negative Quick Prompts
|
||||
if (data["Negative Quick Prompts"]) {
|
||||
data["Negative Quick Prompts"].forEach(prompt => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.classList.add('btn', 'btn-secondary', 'btn-sm', 'me-2', 'mb-2');
|
||||
button.innerText = prompt.label;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
addNegativeKeyword(button, prompt.value);
|
||||
});
|
||||
|
||||
negativeKeywordsContainer.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
// Load Other Quick Prompt Categories (e.g., Halloween, Christmas)
|
||||
for (const category in data) {
|
||||
if (category !== "Positive Quick Prompts" && category !== "Negative Quick Prompts") {
|
||||
const section = document.createElement('div');
|
||||
section.classList.add('mb-3');
|
||||
|
||||
const heading = document.createElement('label');
|
||||
heading.classList.add('form-label');
|
||||
heading.innerText = category;
|
||||
|
||||
const buttonsContainer = document.createElement('div');
|
||||
buttonsContainer.classList.add('keyword-suggestions');
|
||||
|
||||
// Generate buttons for each prompt
|
||||
data[category].forEach(prompt => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.classList.add('btn', 'btn-secondary', 'btn-sm', 'me-2', 'mb-2');
|
||||
button.innerText = prompt.label;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
addPositiveKeyword(button, prompt.value);
|
||||
});
|
||||
|
||||
buttonsContainer.appendChild(button);
|
||||
});
|
||||
|
||||
section.appendChild(heading);
|
||||
section.appendChild(buttonsContainer);
|
||||
quickPromptsContainer.appendChild(section);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error loading quick prompts:", error);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to add keywords to input fields and disable buttons after selection
|
||||
window.addPositiveKeyword = function (button, keyword) {
|
||||
const positiveInput = document.getElementById("positivePrompt");
|
||||
positiveInput.value = `${positiveInput.value} ${keyword}`.trim();
|
||||
button.disabled = true; // Disable button after adding keyword
|
||||
};
|
||||
|
||||
window.addNegativeKeyword = function (button, keyword) {
|
||||
const negativeInput = document.getElementById("negativePrompt");
|
||||
negativeInput.value = `${negativeInput.value} ${keyword}`.trim();
|
||||
button.disabled = true; // Disable button after adding keyword
|
||||
};
|
||||
|
||||
// Function to ask the LLM for a creative prompt
|
||||
askLLMButton.addEventListener("click", function () {
|
||||
const positivePrompt = document.getElementById("positivePrompt").value;
|
||||
|
||||
let promptToSend = positivePrompt.trim();
|
||||
if (!promptToSend) {
|
||||
promptToSend = "Generate a general creative idea.";
|
||||
}
|
||||
|
||||
// Disable button and show spinner
|
||||
askLLMButton.disabled = true;
|
||||
askLLMSpinner.classList.remove("d-none");
|
||||
askLLMText.innerText = "Processing...";
|
||||
|
||||
fetch("/ask_llm/", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ positive_prompt: promptToSend })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
llmResponseTextarea.value = data.assistant_reply;
|
||||
useLLMResponseButton.classList.remove("d-none"); // Show the button to use the LLM's response
|
||||
|
||||
// Re-enable button and hide spinner
|
||||
askLLMButton.disabled = false;
|
||||
askLLMSpinner.classList.add("d-none");
|
||||
askLLMText.innerText = "Ask LLM for Creative Idea";
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error getting LLM response:", error);
|
||||
askLLMButton.disabled = false;
|
||||
askLLMSpinner.classList.add("d-none");
|
||||
askLLMText.innerText = "Ask LLM for Creative Idea";
|
||||
});
|
||||
});
|
||||
|
||||
// Function to use the LLM's creative response as the positive prompt
|
||||
useLLMResponseButton.addEventListener("click", function () {
|
||||
const llmResponse = llmResponseTextarea.value;
|
||||
document.getElementById("positivePrompt").value = llmResponse;
|
||||
showNotification("LLM's creative prompt has been applied!");
|
||||
});
|
||||
|
||||
// Form submission for generating image
|
||||
form.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
// Reset the timer and disable the Generate button
|
||||
generateBtn.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
buttonText.innerText = "Generating...";
|
||||
startTimer(); // Start timer with a reset
|
||||
|
||||
// Get input values
|
||||
const positivePrompt = document.getElementById("positivePrompt").value;
|
||||
const negativePrompt = document.getElementById("negativePrompt").value;
|
||||
const steps = document.getElementById("steps").value;
|
||||
const width = document.getElementById("width").value;
|
||||
const height = document.getElementById("height").value;
|
||||
|
||||
// Post image generation request
|
||||
fetch("/generate_images/", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
positive_prompt: positivePrompt,
|
||||
negative_prompt: negativePrompt,
|
||||
steps: parseInt(steps),
|
||||
width: parseInt(width),
|
||||
height: parseInt(height)
|
||||
})
|
||||
})
|
||||
.then(response => response.blob())
|
||||
.then(imageBlob => {
|
||||
const imageUrl = URL.createObjectURL(imageBlob);
|
||||
generatedImage.src = imageUrl;
|
||||
generatedImage.classList.remove("d-none");
|
||||
|
||||
// Cache the image in imageHistory and update the index
|
||||
imageHistory.push(imageUrl);
|
||||
currentImageIndex = imageHistory.length - 1;
|
||||
updateImageNavigation();
|
||||
scrollToImage(); // Automatically scroll to the image
|
||||
|
||||
// Display the selected prompts
|
||||
positivePromptDisplay.innerText = positivePrompt;
|
||||
negativePromptDisplay.innerText = negativePrompt;
|
||||
promptDisplay.classList.remove("d-none");
|
||||
|
||||
// Reset the button and timer
|
||||
generateBtn.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
buttonText.innerText = "Generate Image";
|
||||
stopTimer();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error generating image:", error);
|
||||
generateBtn.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
buttonText.innerText = "Generate Image";
|
||||
stopTimer();
|
||||
showNotification("Failed to generate image. Please try again.", "danger");
|
||||
});
|
||||
});
|
||||
|
||||
// Function to update image navigation visibility and state
|
||||
function updateImageNavigation() {
|
||||
if (imageHistory.length > 1) {
|
||||
imageNavigation.classList.remove("d-none");
|
||||
} else {
|
||||
imageNavigation.classList.add("d-none");
|
||||
}
|
||||
|
||||
prevImageBtn.disabled = currentImageIndex <= 0;
|
||||
nextImageBtn.disabled = currentImageIndex >= imageHistory.length - 1;
|
||||
}
|
||||
|
||||
// Previous and Next Image Navigation
|
||||
prevImageBtn.addEventListener("click", function () {
|
||||
if (currentImageIndex > 0) {
|
||||
currentImageIndex--;
|
||||
generatedImage.src = imageHistory[currentImageIndex];
|
||||
}
|
||||
updateImageNavigation();
|
||||
});
|
||||
|
||||
nextImageBtn.addEventListener("click", function () {
|
||||
if (currentImageIndex < imageHistory.length - 1) {
|
||||
currentImageIndex++;
|
||||
generatedImage.src = imageHistory[currentImageIndex];
|
||||
}
|
||||
updateImageNavigation();
|
||||
});
|
||||
|
||||
// Load quick prompts when the page is loaded
|
||||
loadQuickPrompts();
|
||||
});
|
||||
82
On host/DreamCanvas/ui/styles.css
Normal file
82
On host/DreamCanvas/ui/styles.css
Normal file
@@ -0,0 +1,82 @@
|
||||
body {
|
||||
background-color: #1e1e1e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #f7c04a;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: #f7c04a;
|
||||
}
|
||||
|
||||
.keyword-suggestions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#generatedImage {
|
||||
border: 2px solid #f7c04a;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#promptDisplay {
|
||||
background-color: #292929;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #f7c04a;
|
||||
}
|
||||
|
||||
#positivePromptDisplay {
|
||||
color: #00ff00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#negativePromptDisplay {
|
||||
color: #ff3333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#prevImage, #nextImage {
|
||||
border-color: #f7c04a; /* Gold border */
|
||||
background-color: transparent; /* Transparent background */
|
||||
color: #f7c04a; /* Gold text color */
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
#prevImage:hover, #nextImage:hover {
|
||||
background-color: #f7c04a; /* Gold background on hover */
|
||||
color: #1e1e1e; /* Dark text on hover to contrast */
|
||||
}
|
||||
|
||||
#llmResponse {
|
||||
margin-top: 0; /* Remove any extra margin at the top */
|
||||
padding-top: 0; /* Remove extra padding at the top */
|
||||
line-height: 1.5; /* Adjust line height for better readability */
|
||||
text-align: left; /* Ensure the text aligns to the left */
|
||||
}
|
||||
|
||||
#llmResponse:focus {
|
||||
outline: none; /* Remove the blue outline when focused */
|
||||
box-shadow: 0 0 5px 2px #f7c04a; /* Optional: Add a gold glow when focused */
|
||||
}
|
||||
|
||||
/* Center the title on smaller screens */
|
||||
@media (max-width: 768px) {
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#imageResult {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* New styles for responsive layout and sticky image preview */
|
||||
.sticky-top {
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
}
|
||||
Reference in New Issue
Block a user