update
This commit is contained in:
parent
16c71242d2
commit
c456468b31
7 changed files with 342 additions and 81 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,3 +3,4 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/.venv
|
||||||
|
|
|
||||||
17
run_app.command
Executable file
17
run_app.command
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# /opt/miniconda3/bin/python is required by the user
|
||||||
|
#!/bin/zsh
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
PYTHON_BIN="/opt/miniconda3/bin/python"
|
||||||
|
|
||||||
|
# Create a virtual environment if missing, then activate it
|
||||||
|
if [[ ! -d ".venv" ]]; then
|
||||||
|
"$PYTHON_BIN" -m venv .venv
|
||||||
|
fi
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Ensure dependencies are available (skip reinstall if up-to-date)
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Start the Flask app on port 8888
|
||||||
|
exec .venv/bin/python app.py
|
||||||
136
static/script.js
136
static/script.js
|
|
@ -23,6 +23,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
let cachedReferenceImages = [];
|
let cachedReferenceImages = [];
|
||||||
const imageDisplayArea = document.querySelector('.image-display-area');
|
const imageDisplayArea = document.querySelector('.image-display-area');
|
||||||
const canvasToolbar = document.querySelector('.canvas-toolbar');
|
const canvasToolbar = document.querySelector('.canvas-toolbar');
|
||||||
|
const popupOverlay = document.getElementById('popup-overlay');
|
||||||
|
const popupTitleEl = document.getElementById('popup-title');
|
||||||
|
const popupBodyEl = document.getElementById('popup-body');
|
||||||
|
const popupCloseBtn = document.getElementById('popup-close');
|
||||||
|
const popupButtons = document.querySelectorAll('[data-popup-target]');
|
||||||
const ZOOM_STEP = 0.1;
|
const ZOOM_STEP = 0.1;
|
||||||
const MIN_ZOOM = 0.4;
|
const MIN_ZOOM = 0.4;
|
||||||
const MAX_ZOOM = 4;
|
const MAX_ZOOM = 4;
|
||||||
|
|
@ -31,6 +36,48 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
let isPanning = false;
|
let isPanning = false;
|
||||||
let lastPointer = { x: 0, y: 0 };
|
let lastPointer = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
const infoContent = {
|
||||||
|
title: 'Thông tin',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Liên hệ',
|
||||||
|
items: [
|
||||||
|
'Người tạo: Phạm Hưng',
|
||||||
|
'Group: <a href="https://www.facebook.com/groups/stablediffusion.vn/" target="_blank" rel="noreferrer">SDVN - Cộng đồng AI Art</a>',
|
||||||
|
'Website: <a href="https://sdvn.vn" target="_blank" rel="noreferrer">sdvn.vn</a>',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const docsContent = {
|
||||||
|
title: 'Phím tắt và mẹo',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
heading: 'Phím tắt',
|
||||||
|
items: [
|
||||||
|
'Ctrl/Cmd + Enter → tạo ảnh mới',
|
||||||
|
'D → tải ảnh hiện tại',
|
||||||
|
'R → reset zoom/pan vùng hiển thị ảnh',
|
||||||
|
'Esc → đóng popup thông tin/docs',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Thao tác nhanh',
|
||||||
|
items: [
|
||||||
|
'Kéo ảnh từ lịch sử vào ô tham chiếu để tái sử dụng',
|
||||||
|
'Tùy chỉnh tỉ lệ và độ phân giải trước khi nhấn Generate',
|
||||||
|
'API key và prompt được lưu để lần sau không phải nhập lại',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const POPUP_CONTENT = {
|
||||||
|
info: infoContent,
|
||||||
|
docs: docsContent,
|
||||||
|
};
|
||||||
|
|
||||||
// Load gallery on start
|
// Load gallery on start
|
||||||
loadSettings();
|
loadSettings();
|
||||||
initializeImageInputs();
|
initializeImageInputs();
|
||||||
|
|
@ -193,6 +240,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
const img = document.createElement('img');
|
const img = document.createElement('img');
|
||||||
img.src = withCacheBuster(imageUrl);
|
img.src = withCacheBuster(imageUrl);
|
||||||
img.loading = 'lazy';
|
img.loading = 'lazy';
|
||||||
|
img.draggable = true;
|
||||||
|
img.dataset.source = imageUrl;
|
||||||
|
img.addEventListener('dragstart', event => {
|
||||||
|
event.dataTransfer?.setData('text/uri-list', imageUrl);
|
||||||
|
event.dataTransfer?.setData('text/plain', imageUrl);
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
event.dataTransfer.effectAllowed = 'copy';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
div.appendChild(img);
|
div.appendChild(img);
|
||||||
galleryGrid.appendChild(div);
|
galleryGrid.appendChild(div);
|
||||||
|
|
@ -456,12 +512,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
slot.classList.remove('drag-over');
|
slot.classList.remove('drag-over');
|
||||||
});
|
});
|
||||||
|
|
||||||
slot.addEventListener('drop', event => {
|
slot.addEventListener('drop', async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
slot.classList.remove('drag-over');
|
slot.classList.remove('drag-over');
|
||||||
const file = event.dataTransfer?.files?.[0];
|
const file = event.dataTransfer?.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
handleSlotFile(index, file);
|
handleSlotFile(index, file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageUrl = event.dataTransfer?.getData('text/uri-list')
|
||||||
|
|| event.dataTransfer?.getData('text/plain');
|
||||||
|
if (imageUrl) {
|
||||||
|
await handleSlotDropFromHistory(index, imageUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -493,6 +556,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleSlotDropFromHistory(index, imageUrl) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(withCacheBuster(imageUrl));
|
||||||
|
if (!response.ok) {
|
||||||
|
console.warn('Failed to fetch history image', response.statusText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
const name = imageUrl.split('/').pop()?.split('?')[0] || `history-${index + 1}.png`;
|
||||||
|
const type = blob.type || 'image/png';
|
||||||
|
const file = new File([blob], name, { type });
|
||||||
|
handleSlotFile(index, file);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Unable to import history image', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateSlotVisual(index) {
|
function updateSlotVisual(index) {
|
||||||
const slotRecord = imageSlotState[index];
|
const slotRecord = imageSlotState[index];
|
||||||
if (!slotRecord) return;
|
if (!slotRecord) return;
|
||||||
|
|
@ -580,4 +661,57 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
popupButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const target = button.dataset.popupTarget;
|
||||||
|
if (target) {
|
||||||
|
showPopup(target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (popupCloseBtn) {
|
||||||
|
popupCloseBtn.addEventListener('click', closePopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popupOverlay) {
|
||||||
|
popupOverlay.addEventListener('click', event => {
|
||||||
|
if (event.target === popupOverlay) {
|
||||||
|
closePopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', event => {
|
||||||
|
if (event.key === 'Escape' && popupOverlay && !popupOverlay.classList.contains('hidden')) {
|
||||||
|
event.preventDefault();
|
||||||
|
closePopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showPopup(type) {
|
||||||
|
const content = POPUP_CONTENT[type];
|
||||||
|
if (!content || !popupOverlay || !popupBodyEl || !popupTitleEl) return;
|
||||||
|
|
||||||
|
popupTitleEl.textContent = content.title;
|
||||||
|
popupBodyEl.innerHTML = content.sections
|
||||||
|
.map(section => {
|
||||||
|
const items = (section.items || []).map(item => `<li>${item}</li>`).join('');
|
||||||
|
return `
|
||||||
|
<section class="popup-section">
|
||||||
|
<h3>${section.heading}</h3>
|
||||||
|
<ul>${items}</ul>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
popupOverlay.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopup() {
|
||||||
|
if (!popupOverlay) return;
|
||||||
|
popupOverlay.classList.add('hidden');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
BIN
static/sdvn-logo.png
Normal file
BIN
static/sdvn-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
154
static/style.css
154
static/style.css
|
|
@ -63,7 +63,7 @@ body {
|
||||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
@ -111,28 +111,6 @@ body {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-generate {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-generate #generate-btn {
|
|
||||||
padding: 0.4rem 1rem;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
height: 28px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 0.45rem;
|
|
||||||
min-width: 90px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-generate #generate-btn {
|
|
||||||
padding: 0.35rem 0.9rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
.brand {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
|
|
@ -148,18 +126,51 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 0.75rem;
|
font-size: 0.5rem;
|
||||||
background-color: rgba(245, 197, 24, 0.15);
|
/* background-color: rgba(245, 197, 24, 0.15); */
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.2rem 0.3rem;
|
||||||
border-radius: 999px;
|
/* border-radius: 999px; */
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls-section {
|
.controls-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-footer #generate-btn {
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
height: 28px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 0.45rem;
|
||||||
|
min-width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-footer #generate-btn {
|
||||||
|
padding: 0.35rem 0.9rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
|
|
@ -517,6 +528,95 @@ button#generate-btn:disabled {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(3, 3, 10, 0.75);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 50;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-card {
|
||||||
|
width: min(520px, 100%);
|
||||||
|
background: rgba(10, 11, 22, 0.96);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.8);
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-close {
|
||||||
|
border: none;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
color: var(--text-primary);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-body {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-section h3 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-section ul {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.35rem;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-section li {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding-left: 1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-section li::before {
|
||||||
|
content: '•';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* Gallery Section */
|
/* Gallery Section */
|
||||||
.history-section {
|
.history-section {
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>aPix Image Workspace</title>
|
<title>aPix Image Workspace</title>
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='sdvn-logo.png') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
|
@ -15,69 +16,68 @@
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<h1>aPix</h1>
|
<h1>aPix <span class="badge">by SDVN</span></h1>
|
||||||
<span class="badge">Creative Studio</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controls-section">
|
<div class="controls-section">
|
||||||
<div class="input-group">
|
<div class="controls-content">
|
||||||
<label for="api-key">API Key</label>
|
<div class="input-group">
|
||||||
<input type="password" id="api-key" placeholder="Google Cloud API Key">
|
<label for="api-key">API Key</label>
|
||||||
</div>
|
<input type="password" id="api-key" placeholder="Google Cloud API Key">
|
||||||
|
|
||||||
<div class="input-group">
|
|
||||||
<label for="prompt">Prompt</label>
|
|
||||||
<textarea id="prompt" placeholder="Describe your imagination..." rows="6"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group image-inputs">
|
|
||||||
<div class="image-input-header">
|
|
||||||
<label>Reference Images (optional)</label>
|
|
||||||
<span>Drag & drop up to 16 files</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="image-input-grid" class="image-input-grid" aria-live="polite"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label for="aspect-ratio">Aspect Ratio</label>
|
<label for="prompt">Prompt</label>
|
||||||
<select id="aspect-ratio">
|
<textarea id="prompt" placeholder="Describe your imagination..." rows="6"></textarea>
|
||||||
<option value="Auto">Auto (Default)</option>
|
</div>
|
||||||
<option value="1:1">1:1 (Square)</option>
|
|
||||||
<option value="16:9">16:9 (Widescreen)</option>
|
|
||||||
<option value="4:3">4:3 (Standard)</option>
|
|
||||||
<option value="3:4">3:4 (Portrait)</option>
|
|
||||||
<option value="9:16">9:16 (Mobile)</option>
|
|
||||||
<option value="2:3">2:3</option>
|
|
||||||
<option value="3:2">3:2</option>
|
|
||||||
<option value="4:5">4:5</option>
|
|
||||||
<option value="5:4">5:4</option>
|
|
||||||
<option value="21:9">21:9 (Cinema)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group image-inputs">
|
||||||
<label for="resolution">Resolution</label>
|
<div class="image-input-header">
|
||||||
<select id="resolution">
|
<label>Reference Images (optional)</label>
|
||||||
<option value="2K" selected>2K</option>
|
<span>Drag & drop up to 16 files</span>
|
||||||
<option value="1K">1K</option>
|
</div>
|
||||||
<option value="4K">4K</option>
|
<div id="image-input-grid" class="image-input-grid" aria-live="polite"></div>
|
||||||
</select>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="aspect-ratio">Aspect Ratio</label>
|
||||||
|
<select id="aspect-ratio">
|
||||||
|
<option value="Auto">Auto (Default)</option>
|
||||||
|
<option value="1:1">1:1 (Square)</option>
|
||||||
|
<option value="16:9">16:9 (Widescreen)</option>
|
||||||
|
<option value="4:3">4:3 (Standard)</option>
|
||||||
|
<option value="3:4">3:4 (Portrait)</option>
|
||||||
|
<option value="9:16">9:16 (Mobile)</option>
|
||||||
|
<option value="2:3">2:3</option>
|
||||||
|
<option value="3:2">3:2</option>
|
||||||
|
<option value="4:5">4:5</option>
|
||||||
|
<option value="5:4">5:4</option>
|
||||||
|
<option value="21:9">21:9 (Cinema)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="resolution">Resolution</label>
|
||||||
|
<select id="resolution">
|
||||||
|
<option value="2K" selected>2K</option>
|
||||||
|
<option value="1K">1K</option>
|
||||||
|
<option value="4K">4K</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="controls-footer">
|
||||||
|
<button id="generate-btn">
|
||||||
|
<span>Generate</span>
|
||||||
|
<div class="btn-shine"></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<div class="content-area">
|
<div class="content-area">
|
||||||
<div class="top-toolbar">
|
<div class="top-toolbar">
|
||||||
<div class="toolbar-info">
|
<div class="toolbar-info">
|
||||||
<button type="button" class="toolbar-info-btn">Info</button>
|
<button type="button" class="toolbar-info-btn" data-popup-target="docs">Docs</button>
|
||||||
<button type="button" class="toolbar-info-btn">Docs</button>
|
<button type="button" class="toolbar-info-btn" data-popup-target="info">Info</button>
|
||||||
<button type="button" class="toolbar-info-btn" disabled>Preview</button>
|
|
||||||
</div>
|
|
||||||
<div class="toolbar-generate">
|
|
||||||
<button id="generate-btn">
|
|
||||||
<span>Generate</span>
|
|
||||||
<div class="btn-shine"></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
|
|
@ -120,6 +120,15 @@
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="popup-overlay" class="popup-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="popup-title">
|
||||||
|
<div class="popup-card">
|
||||||
|
<header class="popup-header">
|
||||||
|
<h2 id="popup-title"></h2>
|
||||||
|
<button id="popup-close" type="button" class="popup-close" aria-label="Close">×</button>
|
||||||
|
</header>
|
||||||
|
<div id="popup-body" class="popup-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue