This commit is contained in:
2026-01-28 16:54:06 +01:00
parent 213c2836f9
commit 493eb0ed90
73 changed files with 0 additions and 0 deletions

97
DreamCanvas/ui/index.html Normal file
View 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
DreamCanvas/ui/script.js Normal file
View 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
DreamCanvas/ui/styles.css Normal file
View 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;
}