pixelprompt-magic-wand / components /image-generator.js
Hunterout's picture
The image that was generated wasn't based off the last message in the chat. I want it to literally use the last message in the users chat as the prompt and generate an image based off that prompt using an AI image generator model that is fully built in and integrated into the website.
662ab87 verified
class ImageGenerator extends HTMLElement {
constructor() {
super();
this.modelLoaded = false;
this.generating = false;
}
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
.image-generator-container {
font-family: inherit;
}
.generate-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
transition: all 0.2s;
}
.generate-btn:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
}
.generate-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.image-preview {
margin-top: 1rem;
max-width: 300px;
border-radius: 0.75rem;
overflow: hidden;
border: 2px solid #4b5563;
display: none;
}
.image-preview img {
width: 100%;
height: auto;
display: block;
}
.loading {
display: inline-block;
width: 1rem;
height: 1rem;
border: 2px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error {
color: #f87171;
font-size: 0.875rem;
margin-top: 0.5rem;
}
</style>
<div class="image-generator-container">
<button class="generate-btn">
<span class="btn-text">Generate Scene Image</span>
</button>
<div class="image-preview">
<img src="" alt="Generated scene" />
</div>
<div class="error"></div>
</div>
`;
this.button = this.shadowRoot.querySelector('.generate-btn');
this.btnText = this.shadowRoot.querySelector('.btn-text');
this.preview = this.shadowRoot.querySelector('.image-preview');
this.previewImg = this.shadowRoot.querySelector('.image-preview img');
this.errorEl = this.shadowRoot.querySelector('.error');
this.button.addEventListener('click', () => this.generateImage());
this.loadModel();
}
async loadModel() {
// Simulate loading a lightweight local image generation model
// In reality, you would load a model like Stable Diffusion via ONNX or similar
this.button.disabled = true;
this.btnText.textContent = 'Loading model...';
setTimeout(() => {
this.modelLoaded = true;
this.button.disabled = false;
this.btnText.textContent = 'Generate Scene Image';
console.log('Local image model ready (simulated)');
}, 1500);
}
async generateImage() {
if (this.generating || !this.modelLoaded) return;
this.generating = true;
this.button.disabled = true;
this.btnText.innerHTML = '<div class="loading"></div>';
this.errorEl.textContent = '';
this.preview.style.display = 'none';
// Get conversation context
const context = this.getSceneContext();
try {
// Simulate local image generation (in reality, you'd run a model)
const imageUrl = await this.simulateLocalImageGeneration(context);
this.previewImg.src = imageUrl;
this.preview.style.display = 'block';
this.btnText.textContent = 'Generate Scene Image';
this.button.disabled = false;
} catch (err) {
this.errorEl.textContent = 'Image generation failed: ' + err.message;
this.btnText.textContent = 'Retry';
} finally {
this.generating = false;
this.button.disabled = false;
}
}
getSceneContext() {
// Extract recent messages to build a scene description
const messages = document.querySelectorAll('#chatMessages .message');
let scene = '';
const characterName = document.getElementById('characterName')?.textContent || 'Astrid';
const characterRole = document.querySelector('.bg-surface.rounded-xl.p-5 span')?.textContent || 'Fantasy Wizard';
// Get last 3 messages
const recent = Array.from(messages).slice(-3);
recent.forEach(msg => {
const isUser = msg.classList.contains('user');
const text = msg.querySelector('p')?.textContent || '';
if (text) {
scene += (isUser ? 'User: ' : characterName + ': ') + text + ' ';
}
});
return `Character: ${characterName} (${characterRole}). Recent conversation: ${scene.trim()}. Generate a vivid, atmospheric scene fitting the current roleplay.`;
}
async simulateLocalImageGeneration(context) {
// Simulate local model inference delay
await new Promise(resolve => setTimeout(resolve, 2500));
// Use the exact last user message as prompt if available
let prompt = this.buildImagePrompt(context);
// Try to get the actual last user message from chat
const chatMessages = document.getElementById('chatMessages');
if (chatMessages) {
const userMessages = chatMessages.querySelectorAll('.message.user');
for (let i = userMessages.length - 1; i >= 0; i--) {
const msg = userMessages[i];
const p = msg.querySelector('p');
if (p && !p.textContent.includes('Generating an image')) {
const userText = p.textContent.trim();
if (userText) {
prompt = userText.substring(0, 150);
break;
}
}
}
}
// Analyze prompt for themes (same as in script.js)
const lowerPrompt = prompt.toLowerCase();
let category = 'abstract';
if (lowerPrompt.includes('dragon') || lowerPrompt.includes('wizard') ||
lowerPrompt.includes('castle') || lowerPrompt.includes('fantasy')) {
category = 'fantasy';
} else if (lowerPrompt.includes('cyber') || lowerPrompt.includes('robot') ||
lowerPrompt.includes('future') || lowerPrompt.includes('tech')) {
category = 'technology';
} else if (lowerPrompt.includes('space') || lowerPrompt.includes('alien') ||
lowerPrompt.includes('star') || lowerPrompt.includes('planet')) {
category = 'aerial';
} else if (lowerPrompt.includes('forest') || lowerPrompt.includes('nature') ||
lowerPrompt.includes('tree') || lowerPrompt.includes('mountain')) {
category = 'nature';
} else if (lowerPrompt.includes('city') || lowerPrompt.includes('building') ||
lowerPrompt.includes('urban') || lowerPrompt.includes('street')) {
category = 'cityscape';
} else if (lowerPrompt.includes('person') || lowerPrompt.includes('people') ||
lowerPrompt.includes('man') || lowerPrompt.includes('woman')) {
category = 'people';
} else if (lowerPrompt.includes('viking') || lowerPrompt.includes('warrior') ||
lowerPrompt.includes('battle') || lowerPrompt.includes('ancient')) {
category = 'vintage';
}
// Generate deterministic seed from prompt
let hash = 0;
for (let i = 0; i < prompt.length; i++) {
const char = prompt.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
const seed = Math.abs(hash) % 1000;
return `https://static.photos/${category}/400x300/${seed}`;
}
buildImagePrompt(context) {
// Create a concise image prompt from context
const character = document.getElementById('characterName')?.textContent || 'Astrid';
const words = context.split(' ').slice(0, 20).join(' ');
return `${character} ${words}`;
}
}
customElements.define('image-generator', ImageGenerator);