MoShow's picture
I need this app to deliver High resolution products, HD, Ulte-HD, 40K stats
2f9cc52 verified
class CustomPromptInput extends HTMLElement {
constructor() {
super();
this.mode = 'image';
}
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.render();
this.attachEvents();
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
width: 100%;
}
.prompt-container {
background: rgba(30, 41, 59, 0.6);
backdrop-filter: blur(20px);
border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 24px;
padding: 1.5rem;
transition: all 0.3s ease;
}
.prompt-container:focus-within {
border-color: #0ea5e9;
box-shadow: 0 0 60px rgba(14, 165, 233, 0.2);
}
.mode-selector {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.mode-btn {
padding: 0.5rem 1rem;
border-radius: 9999px;
border: 1px solid transparent;
background: rgba(15, 23, 42, 0.5);
color: #94a3b8;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.mode-btn:hover {
background: rgba(30, 41, 59, 0.8);
color: #e2e8f0;
}
.mode-btn.active {
background: linear-gradient(135deg, #0ea5e9, #d946ef);
color: white;
border-color: transparent;
}
.input-wrapper {
position: relative;
}
textarea {
width: 100%;
min-height: 120px;
background: transparent;
border: none;
color: #e2e8f0;
font-size: 1.125rem;
line-height: 1.6;
resize: none;
outline: none;
font-family: inherit;
}
textarea::placeholder {
color: #64748b;
}
.input-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(148, 163, 184, 0.1);
}
.input-actions {
display: flex;
gap: 0.75rem;
}
.action-btn {
width: 40px;
height: 40px;
border-radius: 12px;
border: 1px solid rgba(148, 163, 184, 0.2);
background: rgba(15, 23, 42, 0.5);
color: #94a3b8;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.action-btn:hover {
background: rgba(30, 41, 59, 0.8);
color: #0ea5e9;
border-color: #0ea5e9;
}
.generate-btn {
padding: 0.875rem 2rem;
background: linear-gradient(135deg, #0ea5e9, #d946ef);
border: none;
border-radius: 16px;
color: white;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.75rem;
}
.generate-btn:hover:not(:disabled) {
transform: scale(1.02);
box-shadow: 0 20px 60px rgba(14, 165, 233, 0.4);
}
.generate-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.generate-btn.loading {
background: linear-gradient(90deg, #0ea5e9, #d946ef, #0ea5e9);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.char-count {
font-size: 0.75rem;
color: #64748b;
}
.quality-presets {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.quality-preset {
padding: 0.5rem 1rem;
background: rgba(30, 41, 59, 0.6);
border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 12px;
color: #94a3b8;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
}
.quality-preset:hover {
background: rgba(30, 41, 59, 0.8);
border-color: #0ea5e9;
color: #e2e8f0;
}
.quality-preset.active {
background: linear-gradient(135deg, #0ea5e9, #d946ef);
border-color: transparent;
color: white;
}
.quality-preset .res {
font-size: 0.875rem;
font-weight: 700;
}
.quality-preset .label {
font-size: 0.65rem;
opacity: 0.8;
text-transform: uppercase;
}
.suggestions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.suggestion-chip {
padding: 0.375rem 0.875rem;
background: rgba(14, 165, 233, 0.1);
border: 1px solid rgba(14, 165, 233, 0.2);
border-radius: 9999px;
color: #7dd3fc;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s ease;
}
.suggestion-chip:hover {
background: rgba(14, 165, 233, 0.2);
border-color: #0ea5e9;
}
</style>
<div class="prompt-container">
<div class="mode-selector">
<button class="mode-btn ${this.mode === 'image' ? 'active' : ''}" data-mode="image">
<i data-feather="image" style="width: 14px; height: 14px;"></i>
Image
</button>
<button class="mode-btn ${this.mode === 'video' ? 'active' : ''}" data-mode="video">
<i data-feather="video" style="width: 14px; height: 14px;"></i>
Video
</button>
<button class="mode-btn ${this.mode === 'gif' ? 'active' : ''}" data-mode="gif">
<i data-feather="film" style="width: 14px; height: 14px;"></i>
GIF
</button>
<button class="mode-btn ${this.mode === '3d' ? 'active' : ''}" data-mode="3d">
<i data-feather="box" style="width: 14px; height: 14px;"></i>
3D Model
</button>
</div>
<div class="input-wrapper">
<textarea
id="prompt-input"
placeholder="Describe what you want to create... (e.g., 'A futuristic cityscape with flying cars and neon lights at sunset')"
maxlength="500"
></textarea>
</div>
<div class="input-footer">
<div class="input-actions">
<button class="action-btn" title="Upload reference image">
<i data-feather="upload" style="width: 18px; height: 18px;"></i>
</button>
<button class="action-btn" title="Random prompt">
<i data-feather="shuffle" style="width: 18px; height: 18px;"></i>
</button>
<button class="action-btn" title="Voice input">
<i data-feather="mic" style="width: 18px; height: 18px;"></i>
</button>
</div>
<div style="display: flex; align-items: center; gap: 1rem;">
<span class="char-count"><span id="char-count">0</span>/500</span>
<button class="generate-btn" id="generate-btn">
<i data-feather="sparkles" style="width: 20px; height: 20px;"></i>
Generate
</button>
</div>
</div>
<div class="quality-presets">
<button class="quality-preset" data-w="1920" data-h="1080">
<span class="res">FHD</span>
<span class="label">Fast</span>
</button>
<button class="quality-preset active" data-w="3840" data-h="2160">
<span class="res">4K</span>
<span class="label">Balanced</span>
</button>
<button class="quality-preset" data-w="7680" data-h="4320">
<span class="res">8K</span>
<span class="label">Quality</span>
</button>
<button class="quality-preset" data-w="15360" data-h="8640">
<span class="res">16K</span>
<span class="label">Ultra</span>
</button>
</div>
<div class="suggestions">
<span class="suggestion-chip">8K cinematic</span>
<span class="suggestion-chip">HDR landscape</span>
<span class="suggestion-chip">Ultra-detailed portrait</span>
<span class="suggestion-chip">Photorealistic 16K</span>
<span class="suggestion-chip">IMAX quality</span>
</div>
</div>
`;
}
attachEvents() {
const textarea = this.shadowRoot.getElementById('prompt-input');
const charCount = this.shadowRoot.getElementById('char-count');
const generateBtn = this.shadowRoot.getElementById('generate-btn');
const modeBtns = this.shadowRoot.querySelectorAll('.mode-btn');
const suggestions = this.shadowRoot.querySelectorAll('.suggestion-chip');
// Character count
textarea.addEventListener('input', () => {
charCount.textContent = textarea.value.length;
});
// Mode selection
modeBtns.forEach(btn => {
btn.addEventListener('click', () => {
modeBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
this.mode = btn.dataset.mode;
});
});
// Generate
generateBtn.addEventListener('click', () => {
const prompt = textarea.value.trim();
if (!prompt) {
textarea.focus();
return;
}
generateBtn.disabled = true;
generateBtn.classList.add('loading');
generateBtn.innerHTML = `
<i data-feather="loader" style="width: 20px; height: 20px; animation: spin 1s linear infinite;"></i>
Generating...
`;
document.dispatchEvent(new CustomEvent('submitPrompt', {
detail: {
prompt,
mode: this.mode,
settings: {
...window.VortexAI?.state?.settings,
width: this.selectedResolution?.width || 3840,
height: this.selectedResolution?.height || 2160
}
}
}));
});
// Quality presets
const qualityPresets = this.shadowRoot.querySelectorAll('.quality-preset');
qualityPresets.forEach(preset => {
preset.addEventListener('click', () => {
qualityPresets.forEach(p => p.classList.remove('active'));
preset.classList.add('active');
// Store selected resolution
this.selectedResolution = {
width: parseInt(preset.dataset.w),
height: parseInt(preset.dataset.h)
};
});
});
// Set default resolution
this.selectedResolution = { width: 3840, height: 2160 };
// Suggestions
suggestions.forEach(chip => {
chip.addEventListener('click', () => {
textarea.value = chip.textContent;
charCount.textContent = textarea.value.length;
textarea.focus();
});
});
// Random prompt
const randomBtn = this.shadowRoot.querySelector('[title="Random prompt"]');
const randomPrompts = [
'A cyberpunk samurai standing on a rooftop in Tokyo, neon lights reflecting on wet streets',
'An underwater city with bioluminescent architecture and manta rays swimming through',
'A steampunk airship battle above Victorian London at sunset',
'Crystalline forest with light refractions creating rainbow pathways',
'Mars colony interior with hydroponic gardens and Earth view through windows'
];
randomBtn.addEventListener('click', () => {
const random = randomPrompts[Math.floor(Math.random() * randomPrompts.length)];
textarea.value = random;
charCount.textContent = random.length;
});
}
}
customElements.define('custom-prompt-input', CustomPromptInput);