Spaces:
Running on Zero
Running on Zero
update UI./
Browse files- index.html +237 -5
index.html
CHANGED
|
@@ -152,6 +152,16 @@ body{
|
|
| 152 |
repeating-conic-gradient(#0c0c0c 0% 25%, #050505 0% 50%) 0 0/24px 24px;
|
| 153 |
padding: 24px;
|
| 154 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
#draw-canvas {
|
| 156 |
background: #ffffff;
|
| 157 |
border: 1px solid var(--border2);
|
|
@@ -181,6 +191,60 @@ body{
|
|
| 181 |
display: none;
|
| 182 |
}
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
/* Loader */
|
| 185 |
.modern-loader{
|
| 186 |
display:none;position:absolute;inset:0;background:rgba(0,0,0,.92);
|
|
@@ -429,6 +493,13 @@ body{
|
|
| 429 |
<svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
| 430 |
</button>
|
| 431 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
<div class="rail-sep"></div>
|
| 433 |
<div class="rail-label">Result</div>
|
| 434 |
<button class="rail-btn" id="btn-download" title="Download Result" disabled>
|
|
@@ -454,7 +525,11 @@ body{
|
|
| 454 |
<div class="loader-bar-track"><div class="loader-bar-fill" id="loader-bar-fill"></div></div>
|
| 455 |
</div>
|
| 456 |
|
| 457 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
<img id="result-img" alt="Result Image" style="display:none; aspect-ratio: 16 / 9;">
|
| 459 |
</div>
|
| 460 |
</div>
|
|
@@ -544,6 +619,8 @@ const canvas = document.getElementById('draw-canvas');
|
|
| 544 |
const ctx = canvas.getContext('2d');
|
| 545 |
const resultImg = document.getElementById('result-img');
|
| 546 |
const btnDownload = document.getElementById('btn-download');
|
|
|
|
|
|
|
| 547 |
|
| 548 |
const btnBrush = document.getElementById('btn-brush');
|
| 549 |
const btnEraser = document.getElementById('btn-eraser');
|
|
@@ -632,6 +709,7 @@ function updateCanvasRatio(ratio) {
|
|
| 632 |
setupCanvas(); // Clear and reset canvas on ratio change
|
| 633 |
undoStack = []; // Clear history on ratio change
|
| 634 |
redoStack = [];
|
|
|
|
| 635 |
saveState(); // Save initial blank state
|
| 636 |
}
|
| 637 |
|
|
@@ -659,6 +737,8 @@ function getPos(e) {
|
|
| 659 |
}
|
| 660 |
|
| 661 |
function startDrawing(e) {
|
|
|
|
|
|
|
| 662 |
e.preventDefault();
|
| 663 |
isDrawing = true;
|
| 664 |
const pos = getPos(e);
|
|
@@ -739,6 +819,7 @@ sizeSlider.addEventListener('input', (e) => {
|
|
| 739 |
btnClear.addEventListener('click', () => {
|
| 740 |
setupCanvas();
|
| 741 |
saveState();
|
|
|
|
| 742 |
showToast('Canvas cleared', 'info');
|
| 743 |
});
|
| 744 |
|
|
@@ -767,16 +848,141 @@ btnDownload.addEventListener('click', () => {
|
|
| 767 |
}
|
| 768 |
});
|
| 769 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 770 |
/* ββ UI Toggles ββ */
|
| 771 |
function setViewMode(mode) {
|
| 772 |
if (mode === 'result') {
|
| 773 |
-
|
| 774 |
resultImg.style.display = 'block';
|
| 775 |
segResult.classList.add('active');
|
| 776 |
segInput.classList.remove('active');
|
| 777 |
canvasMeta.textContent = 'Generated Result';
|
| 778 |
} else {
|
| 779 |
-
|
| 780 |
resultImg.style.display = 'none';
|
| 781 |
segResult.classList.remove('active');
|
| 782 |
segInput.classList.add('active');
|
|
@@ -891,7 +1097,33 @@ try {
|
|
| 891 |
showToast('Could not connect to the backend API', 'error');
|
| 892 |
}
|
| 893 |
|
| 894 |
-
/* ββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 895 |
let running = false;
|
| 896 |
let currentJob = null;
|
| 897 |
|
|
@@ -913,7 +1145,7 @@ async function runEdit() {
|
|
| 913 |
showLoader('Submitting to queueβ¦');
|
| 914 |
btnDownload.disabled = true;
|
| 915 |
|
| 916 |
-
const sketch_b64 =
|
| 917 |
const selectedRatio = ratioSelect.value;
|
| 918 |
const [targetW, targetH] = dims[selectedRatio];
|
| 919 |
|
|
|
|
| 152 |
repeating-conic-gradient(#0c0c0c 0% 25%, #050505 0% 50%) 0 0/24px 24px;
|
| 153 |
padding: 24px;
|
| 154 |
}
|
| 155 |
+
|
| 156 |
+
.canvas-container {
|
| 157 |
+
position: relative;
|
| 158 |
+
max-width: 100%;
|
| 159 |
+
max-height: 100%;
|
| 160 |
+
display: flex;
|
| 161 |
+
align-items: center;
|
| 162 |
+
justify-content: center;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
#draw-canvas {
|
| 166 |
background: #ffffff;
|
| 167 |
border: 1px solid var(--border2);
|
|
|
|
| 191 |
display: none;
|
| 192 |
}
|
| 193 |
|
| 194 |
+
/* Sticker Layer */
|
| 195 |
+
#sticker-layer {
|
| 196 |
+
position: absolute;
|
| 197 |
+
top: 0; left: 0; width: 100%; height: 100%;
|
| 198 |
+
pointer-events: none; /* Container itself doesn't block canvas clicks */
|
| 199 |
+
}
|
| 200 |
+
.sticker-wrapper {
|
| 201 |
+
position: absolute;
|
| 202 |
+
pointer-events: auto;
|
| 203 |
+
cursor: move;
|
| 204 |
+
border: 1px dashed transparent;
|
| 205 |
+
user-select: none;
|
| 206 |
+
}
|
| 207 |
+
.sticker-wrapper:hover, .sticker-wrapper.selected {
|
| 208 |
+
border-color: var(--accent);
|
| 209 |
+
}
|
| 210 |
+
.sticker-wrapper img {
|
| 211 |
+
width: 100%;
|
| 212 |
+
height: 100%;
|
| 213 |
+
display: block;
|
| 214 |
+
pointer-events: none;
|
| 215 |
+
}
|
| 216 |
+
.sticker-del-btn {
|
| 217 |
+
position: absolute;
|
| 218 |
+
top: -10px; right: -10px;
|
| 219 |
+
width: 20px; height: 20px;
|
| 220 |
+
background: #ef4444;
|
| 221 |
+
color: #fff;
|
| 222 |
+
border: none;
|
| 223 |
+
border-radius: 50%;
|
| 224 |
+
cursor: pointer;
|
| 225 |
+
display: none;
|
| 226 |
+
align-items: center;
|
| 227 |
+
justify-content: center;
|
| 228 |
+
font-size: 12px;
|
| 229 |
+
z-index: 10;
|
| 230 |
+
}
|
| 231 |
+
.sticker-wrapper:hover .sticker-del-btn, .sticker-wrapper.selected .sticker-del-btn {
|
| 232 |
+
display: flex;
|
| 233 |
+
}
|
| 234 |
+
.resize-handle {
|
| 235 |
+
position: absolute;
|
| 236 |
+
width: 12px; height: 12px;
|
| 237 |
+
background: #fff;
|
| 238 |
+
border: 1px solid #000;
|
| 239 |
+
bottom: -6px; right: -6px;
|
| 240 |
+
cursor: nwse-resize;
|
| 241 |
+
pointer-events: auto;
|
| 242 |
+
display: none;
|
| 243 |
+
}
|
| 244 |
+
.sticker-wrapper:hover .resize-handle, .sticker-wrapper.selected .resize-handle {
|
| 245 |
+
display: block;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
/* Loader */
|
| 249 |
.modern-loader{
|
| 250 |
display:none;position:absolute;inset:0;background:rgba(0,0,0,.92);
|
|
|
|
| 493 |
<svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
| 494 |
</button>
|
| 495 |
|
| 496 |
+
<div class="rail-sep"></div>
|
| 497 |
+
<div class="rail-label">Assets</div>
|
| 498 |
+
<button class="rail-btn" id="btn-sticker" title="Add Sticker / Image">
|
| 499 |
+
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
| 500 |
+
</button>
|
| 501 |
+
<input type="file" id="sticker-input" accept="image/*" style="display:none;">
|
| 502 |
+
|
| 503 |
<div class="rail-sep"></div>
|
| 504 |
<div class="rail-label">Result</div>
|
| 505 |
<button class="rail-btn" id="btn-download" title="Download Result" disabled>
|
|
|
|
| 525 |
<div class="loader-bar-track"><div class="loader-bar-fill" id="loader-bar-fill"></div></div>
|
| 526 |
</div>
|
| 527 |
|
| 528 |
+
<div class="canvas-container" id="canvas-container">
|
| 529 |
+
<canvas id="draw-canvas" width="1024" height="576" class="cursor-brush" style="aspect-ratio: 16 / 9;"></canvas>
|
| 530 |
+
<div id="sticker-layer"></div>
|
| 531 |
+
</div>
|
| 532 |
+
|
| 533 |
<img id="result-img" alt="Result Image" style="display:none; aspect-ratio: 16 / 9;">
|
| 534 |
</div>
|
| 535 |
</div>
|
|
|
|
| 619 |
const ctx = canvas.getContext('2d');
|
| 620 |
const resultImg = document.getElementById('result-img');
|
| 621 |
const btnDownload = document.getElementById('btn-download');
|
| 622 |
+
const stickerLayer = document.getElementById('sticker-layer');
|
| 623 |
+
const canvasContainer = document.getElementById('canvas-container');
|
| 624 |
|
| 625 |
const btnBrush = document.getElementById('btn-brush');
|
| 626 |
const btnEraser = document.getElementById('btn-eraser');
|
|
|
|
| 709 |
setupCanvas(); // Clear and reset canvas on ratio change
|
| 710 |
undoStack = []; // Clear history on ratio change
|
| 711 |
redoStack = [];
|
| 712 |
+
stickerLayer.innerHTML = ''; // Clear stickers on ratio change
|
| 713 |
saveState(); // Save initial blank state
|
| 714 |
}
|
| 715 |
|
|
|
|
| 737 |
}
|
| 738 |
|
| 739 |
function startDrawing(e) {
|
| 740 |
+
// Only draw if clicking directly on canvas, not a sticker
|
| 741 |
+
if (e.target !== canvas) return;
|
| 742 |
e.preventDefault();
|
| 743 |
isDrawing = true;
|
| 744 |
const pos = getPos(e);
|
|
|
|
| 819 |
btnClear.addEventListener('click', () => {
|
| 820 |
setupCanvas();
|
| 821 |
saveState();
|
| 822 |
+
stickerLayer.innerHTML = ''; // Clear stickers too
|
| 823 |
showToast('Canvas cleared', 'info');
|
| 824 |
});
|
| 825 |
|
|
|
|
| 848 |
}
|
| 849 |
});
|
| 850 |
|
| 851 |
+
/* ββ Sticker / Asset Logic ββ */
|
| 852 |
+
const btnSticker = document.getElementById('btn-sticker');
|
| 853 |
+
const stickerInput = document.getElementById('sticker-input');
|
| 854 |
+
let stickers = [];
|
| 855 |
+
|
| 856 |
+
btnSticker.addEventListener('click', () => stickerInput.click());
|
| 857 |
+
|
| 858 |
+
stickerInput.addEventListener('change', (e) => {
|
| 859 |
+
const file = e.target.files[0];
|
| 860 |
+
if (!file) return;
|
| 861 |
+
const reader = new FileReader();
|
| 862 |
+
reader.onload = (ev) => addSticker(ev.target.result);
|
| 863 |
+
reader.readAsDataURL(file);
|
| 864 |
+
e.target.value = ''; // reset input
|
| 865 |
+
});
|
| 866 |
+
|
| 867 |
+
function addSticker(src) {
|
| 868 |
+
const wrapper = document.createElement('div');
|
| 869 |
+
wrapper.className = 'sticker-wrapper';
|
| 870 |
+
|
| 871 |
+
const img = document.createElement('img');
|
| 872 |
+
img.src = src;
|
| 873 |
+
img.draggable = false; // prevent native drag
|
| 874 |
+
wrapper.appendChild(img);
|
| 875 |
+
|
| 876 |
+
const delBtn = document.createElement('button');
|
| 877 |
+
delBtn.className = 'sticker-del-btn';
|
| 878 |
+
delBtn.innerHTML = 'β';
|
| 879 |
+
delBtn.addEventListener('click', (e) => {
|
| 880 |
+
e.stopPropagation();
|
| 881 |
+
wrapper.remove();
|
| 882 |
+
stickers = stickers.filter(s => s.element !== wrapper);
|
| 883 |
+
});
|
| 884 |
+
wrapper.appendChild(delBtn);
|
| 885 |
+
|
| 886 |
+
const resizeHandle = document.createElement('div');
|
| 887 |
+
resizeHandle.className = 'resize-handle';
|
| 888 |
+
wrapper.appendChild(resizeHandle);
|
| 889 |
+
|
| 890 |
+
// Initial size and position (center)
|
| 891 |
+
wrapper.style.width = '150px';
|
| 892 |
+
wrapper.style.height = '150px'; // Will be overwritten by aspect ratio on load
|
| 893 |
+
wrapper.style.left = '40%';
|
| 894 |
+
wrapper.style.top = '40%';
|
| 895 |
+
|
| 896 |
+
stickerLayer.appendChild(wrapper);
|
| 897 |
+
|
| 898 |
+
const stickerObj = { element: wrapper, img: img };
|
| 899 |
+
stickers.push(stickerObj);
|
| 900 |
+
|
| 901 |
+
// Adjust initial height based on image aspect ratio
|
| 902 |
+
img.onload = () => {
|
| 903 |
+
const aspect = img.naturalWidth / img.naturalHeight;
|
| 904 |
+
const w = wrapper.offsetWidth;
|
| 905 |
+
wrapper.style.height = `${w / aspect}px`;
|
| 906 |
+
};
|
| 907 |
+
|
| 908 |
+
// Make draggable
|
| 909 |
+
wrapper.addEventListener('pointerdown', (e) => startDrag(e, stickerObj));
|
| 910 |
+
// Make resizable
|
| 911 |
+
resizeHandle.addEventListener('pointerdown', (e) => startResize(e, stickerObj));
|
| 912 |
+
}
|
| 913 |
+
|
| 914 |
+
function startDrag(e, sticker) {
|
| 915 |
+
e.stopPropagation();
|
| 916 |
+
e.preventDefault();
|
| 917 |
+
sticker.element.classList.add('selected');
|
| 918 |
+
|
| 919 |
+
const rect = sticker.element.getBoundingClientRect();
|
| 920 |
+
const offsetX = e.clientX - rect.left;
|
| 921 |
+
const offsetY = e.clientY - rect.top;
|
| 922 |
+
|
| 923 |
+
function onMove(ev) {
|
| 924 |
+
const layerRect = stickerLayer.getBoundingClientRect();
|
| 925 |
+
let x = ev.clientX - layerRect.left - offsetX;
|
| 926 |
+
let y = ev.clientY - layerRect.top - offsetY;
|
| 927 |
+
|
| 928 |
+
// Keep within bounds roughly
|
| 929 |
+
x = Math.max(-rect.width/2, Math.min(x, layerRect.width - rect.width/2));
|
| 930 |
+
y = Math.max(-rect.height/2, Math.min(y, layerRect.height - rect.height/2));
|
| 931 |
+
|
| 932 |
+
sticker.element.style.left = `${x}px`;
|
| 933 |
+
sticker.element.style.top = `${y}px`;
|
| 934 |
+
}
|
| 935 |
+
function onUp() {
|
| 936 |
+
document.removeEventListener('pointermove', onMove);
|
| 937 |
+
document.removeEventListener('pointerup', onUp);
|
| 938 |
+
sticker.element.classList.remove('selected');
|
| 939 |
+
}
|
| 940 |
+
document.addEventListener('pointermove', onMove);
|
| 941 |
+
document.addEventListener('pointerup', onUp);
|
| 942 |
+
}
|
| 943 |
+
|
| 944 |
+
function startResize(e, sticker) {
|
| 945 |
+
e.stopPropagation();
|
| 946 |
+
e.preventDefault();
|
| 947 |
+
sticker.element.classList.add('selected');
|
| 948 |
+
|
| 949 |
+
const rect = sticker.element.getBoundingClientRect();
|
| 950 |
+
const startW = rect.width;
|
| 951 |
+
const startH = rect.height;
|
| 952 |
+
const startX = e.clientX;
|
| 953 |
+
const startY = e.clientY;
|
| 954 |
+
const aspect = startW / startH;
|
| 955 |
+
|
| 956 |
+
function onMove(ev) {
|
| 957 |
+
let newW = startW + (ev.clientX - startX);
|
| 958 |
+
// Maintain aspect ratio
|
| 959 |
+
let newH = newW / aspect;
|
| 960 |
+
|
| 961 |
+
if (newW < 30) newW = 30;
|
| 962 |
+
if (newH < 30) newH = 30;
|
| 963 |
+
|
| 964 |
+
sticker.element.style.width = `${newW}px`;
|
| 965 |
+
sticker.element.style.height = `${newH}px`;
|
| 966 |
+
}
|
| 967 |
+
function onUp() {
|
| 968 |
+
document.removeEventListener('pointermove', onMove);
|
| 969 |
+
document.removeEventListener('pointerup', onUp);
|
| 970 |
+
sticker.element.classList.remove('selected');
|
| 971 |
+
}
|
| 972 |
+
document.addEventListener('pointermove', onMove);
|
| 973 |
+
document.addEventListener('pointerup', onUp);
|
| 974 |
+
}
|
| 975 |
+
|
| 976 |
/* ββ UI Toggles ββ */
|
| 977 |
function setViewMode(mode) {
|
| 978 |
if (mode === 'result') {
|
| 979 |
+
canvasContainer.style.display = 'none';
|
| 980 |
resultImg.style.display = 'block';
|
| 981 |
segResult.classList.add('active');
|
| 982 |
segInput.classList.remove('active');
|
| 983 |
canvasMeta.textContent = 'Generated Result';
|
| 984 |
} else {
|
| 985 |
+
canvasContainer.style.display = 'flex';
|
| 986 |
resultImg.style.display = 'none';
|
| 987 |
segResult.classList.remove('active');
|
| 988 |
segInput.classList.add('active');
|
|
|
|
| 1097 |
showToast('Could not connect to the backend API', 'error');
|
| 1098 |
}
|
| 1099 |
|
| 1100 |
+
/* ββ Composition & Generation ββ */
|
| 1101 |
+
function getCompositedImage() {
|
| 1102 |
+
const tempCanvas = document.createElement('canvas');
|
| 1103 |
+
tempCanvas.width = canvas.width;
|
| 1104 |
+
tempCanvas.height = canvas.height;
|
| 1105 |
+
const tempCtx = tempCanvas.getContext('2d');
|
| 1106 |
+
|
| 1107 |
+
// Draw the sketch
|
| 1108 |
+
tempCtx.drawImage(canvas, 0, 0);
|
| 1109 |
+
|
| 1110 |
+
// Draw stickers
|
| 1111 |
+
const scaleX = canvas.width / canvas.clientWidth;
|
| 1112 |
+
const scaleY = canvas.height / canvas.clientHeight;
|
| 1113 |
+
|
| 1114 |
+
stickers.forEach(s => {
|
| 1115 |
+
const wrapperRect = s.element.getBoundingClientRect();
|
| 1116 |
+
const layerRect = stickerLayer.getBoundingClientRect();
|
| 1117 |
+
const x = (wrapperRect.left - layerRect.left) * scaleX;
|
| 1118 |
+
const y = (wrapperRect.top - layerRect.top) * scaleY;
|
| 1119 |
+
const w = wrapperRect.width * scaleX;
|
| 1120 |
+
const h = wrapperRect.height * scaleY;
|
| 1121 |
+
tempCtx.drawImage(s.img, x, y, w, h);
|
| 1122 |
+
});
|
| 1123 |
+
|
| 1124 |
+
return tempCanvas.toDataURL('image/png');
|
| 1125 |
+
}
|
| 1126 |
+
|
| 1127 |
let running = false;
|
| 1128 |
let currentJob = null;
|
| 1129 |
|
|
|
|
| 1145 |
showLoader('Submitting to queueβ¦');
|
| 1146 |
btnDownload.disabled = true;
|
| 1147 |
|
| 1148 |
+
const sketch_b64 = getCompositedImage(); // Use composited image
|
| 1149 |
const selectedRatio = ratioSelect.value;
|
| 1150 |
const [targetW, targetH] = dims[selectedRatio];
|
| 1151 |
|