Spaces:
Running on Zero
Running on Zero
update app
Browse files
app.py
CHANGED
|
@@ -165,7 +165,6 @@ def generate_inference_stream(
|
|
| 165 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-4B model not loaded.'})}\n\n"
|
| 166 |
yield "data: [DONE]\n\n"
|
| 167 |
return
|
| 168 |
-
|
| 169 |
messages = [{"role": "user", "content": [
|
| 170 |
{"type": "image", "image": image},
|
| 171 |
{"type": "text", "text": full_prompt},
|
|
@@ -197,7 +196,6 @@ def generate_inference_stream(
|
|
| 197 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-2B model not loaded.'})}\n\n"
|
| 198 |
yield "data: [DONE]\n\n"
|
| 199 |
return
|
| 200 |
-
|
| 201 |
messages = [{"role": "user", "content": [
|
| 202 |
{"type": "image", "image": image},
|
| 203 |
{"type": "text", "text": full_prompt},
|
|
@@ -229,7 +227,6 @@ def generate_inference_stream(
|
|
| 229 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3-VL model not loaded.'})}\n\n"
|
| 230 |
yield "data: [DONE]\n\n"
|
| 231 |
return
|
| 232 |
-
|
| 233 |
messages = [{"role": "user", "content": [
|
| 234 |
{"type": "image", "image": image},
|
| 235 |
{"type": "text", "text": full_prompt},
|
|
@@ -261,7 +258,6 @@ def generate_inference_stream(
|
|
| 261 |
yield f"data: {json.dumps({'chunk': '[Error] LFM-450M model not loaded.'})}\n\n"
|
| 262 |
yield "data: [DONE]\n\n"
|
| 263 |
return
|
| 264 |
-
|
| 265 |
conversation = [{"role": "user", "content": [
|
| 266 |
{"type": "image", "image": image},
|
| 267 |
{"type": "text", "text": full_prompt},
|
|
@@ -290,7 +286,6 @@ def generate_inference_stream(
|
|
| 290 |
yield f"data: {json.dumps({'chunk': '[Error] LFM-1.6B model not loaded.'})}\n\n"
|
| 291 |
yield "data: [DONE]\n\n"
|
| 292 |
return
|
| 293 |
-
|
| 294 |
conversation = [{"role": "user", "content": [
|
| 295 |
{"type": "image", "image": image},
|
| 296 |
{"type": "text", "text": full_prompt},
|
|
@@ -319,7 +314,6 @@ def generate_inference_stream(
|
|
| 319 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-2B-Unredacted-MAX model not loaded.'})}\n\n"
|
| 320 |
yield "data: [DONE]\n\n"
|
| 321 |
return
|
| 322 |
-
|
| 323 |
messages = [{"role": "user", "content": [
|
| 324 |
{"type": "image", "image": image},
|
| 325 |
{"type": "text", "text": full_prompt},
|
|
@@ -442,7 +436,7 @@ async def homepage(request: Request):
|
|
| 442 |
position: relative;
|
| 443 |
width: 1560px;
|
| 444 |
min-height: calc(100vh - 50px);
|
| 445 |
-
height:
|
| 446 |
margin: 0 auto;
|
| 447 |
}
|
| 448 |
|
|
@@ -478,7 +472,7 @@ async def homepage(request: Request):
|
|
| 478 |
.node:hover {
|
| 479 |
box-shadow: 0 10px 34px rgba(0,0,0,0.5), 0 0 0 1px rgba(124,106,247,0.3);
|
| 480 |
}
|
| 481 |
-
.node.fixed-height { height:
|
| 482 |
|
| 483 |
.node-header {
|
| 484 |
background: var(--node-header);
|
|
@@ -524,25 +518,92 @@ async def homepage(request: Request):
|
|
| 524 |
|
| 525 |
input[type="file"] { display: none; }
|
| 526 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
.file-upload {
|
| 528 |
border: 1.5px dashed var(--node-border);
|
| 529 |
-
border-radius: 8px; padding:
|
| 530 |
text-align: center; cursor: pointer;
|
| 531 |
font-size: 12px; color: var(--muted);
|
| 532 |
transition: border-color 0.2s, background 0.2s;
|
| 533 |
background: rgba(255,255,255,0.01);
|
| 534 |
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
| 535 |
}
|
| 536 |
-
.file-upload:hover {
|
|
|
|
|
|
|
|
|
|
| 537 |
.file-upload svg { opacity: 0.5; transition: opacity 0.2s; }
|
| 538 |
.file-upload:hover svg { opacity: 0.9; }
|
| 539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
.img-preview {
|
| 541 |
-
width: 100%;
|
| 542 |
-
|
| 543 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
border: 1px solid var(--node-border);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
}
|
|
|
|
| 546 |
|
| 547 |
select, textarea {
|
| 548 |
width: 100%;
|
|
@@ -569,7 +630,9 @@ async def homepage(request: Request):
|
|
| 569 |
}
|
| 570 |
button.run-btn:hover { opacity: 0.9; }
|
| 571 |
button.run-btn:active { transform: scale(0.98); }
|
| 572 |
-
button.run-btn:disabled {
|
|
|
|
|
|
|
| 573 |
|
| 574 |
.output-box {
|
| 575 |
background: rgba(0,0,0,0.4);
|
|
@@ -582,7 +645,7 @@ async def homepage(request: Request):
|
|
| 582 |
font-family: 'JetBrains Mono', monospace;
|
| 583 |
}
|
| 584 |
|
| 585 |
-
/* Grounding */
|
| 586 |
.ground-canvas-wrap {
|
| 587 |
position: relative; flex: 1;
|
| 588 |
border: 1px solid var(--node-border);
|
|
@@ -665,6 +728,8 @@ async def homepage(request: Request):
|
|
| 665 |
<div class="node-body">
|
| 666 |
<div>
|
| 667 |
<label>Upload Image</label>
|
|
|
|
|
|
|
| 668 |
<div class="file-upload" id="dropZone">
|
| 669 |
<svg width="34" height="34" viewBox="0 0 24 24" fill="none"
|
| 670 |
stroke="#7c6af7" stroke-width="1.5"
|
|
@@ -676,14 +741,34 @@ async def homepage(request: Request):
|
|
| 676 |
<span>Click or drop image here</span>
|
| 677 |
<input type="file" id="fileInput" accept="image/*">
|
| 678 |
</div>
|
| 679 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
</div>
|
| 681 |
</div>
|
| 682 |
<div class="port out" id="port-img-out" style="top:50%;transform:translateY(-50%);"></div>
|
| 683 |
</div>
|
| 684 |
|
| 685 |
<!-- βββ ID 02 : Model Selector βββ -->
|
| 686 |
-
<div class="node fixed-height" id="node-model" style="left:48px; top:
|
| 687 |
<div class="node-header">
|
| 688 |
<span><span class="status-dot" id="dot-model"></span>Model Selector</span>
|
| 689 |
<span class="id">ID: 02</span>
|
|
@@ -755,7 +840,7 @@ async def homepage(request: Request):
|
|
| 755 |
</div>
|
| 756 |
|
| 757 |
<!-- βββ ID 05 : Grounding Visualiser βββ -->
|
| 758 |
-
<div class="node fixed-height" id="node-gnd" style="left:932px; top:
|
| 759 |
<div class="port in" id="port-gnd-in" style="top:50%;transform:translateY(-50%);"></div>
|
| 760 |
<div class="node-header">
|
| 761 |
<span><span class="status-dot" id="dot-gnd"></span>View Grounding</span>
|
|
@@ -799,10 +884,10 @@ function bezier(p1, p2) {
|
|
| 799 |
|
| 800 |
function updateWires() {
|
| 801 |
const wires = [
|
| 802 |
-
['wire-img-task', 'port-img-out',
|
| 803 |
-
['wire-model-task', 'port-model-out',
|
| 804 |
-
['wire-task-out', 'port-task-out',
|
| 805 |
-
['wire-task-gnd', 'port-task-out',
|
| 806 |
];
|
| 807 |
for (const [id, from, to] of wires) {
|
| 808 |
const el = document.getElementById(id);
|
|
@@ -839,26 +924,68 @@ document.addEventListener('scroll', updateWires, true);
|
|
| 839 |
requestAnimationFrame(updateWires);
|
| 840 |
|
| 841 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 842 |
-
// FILE UPLOAD
|
| 843 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 844 |
let currentFile = null;
|
|
|
|
| 845 |
const dropZone = document.getElementById('dropZone');
|
| 846 |
const fileInput = document.getElementById('fileInput');
|
|
|
|
| 847 |
const imgPreview = document.getElementById('imgPreview');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
const dotImg = document.getElementById('dot-img');
|
| 849 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 850 |
function handleFile(file) {
|
| 851 |
if (!file || !file.type.startsWith('image/')) return;
|
| 852 |
currentFile = file;
|
|
|
|
|
|
|
| 853 |
imgPreview.src = URL.createObjectURL(file);
|
| 854 |
-
|
| 855 |
-
dropZone.style.display
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 856 |
dotImg.classList.add('active');
|
| 857 |
requestAnimationFrame(updateWires);
|
| 858 |
}
|
| 859 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 860 |
dropZone.onclick = () => fileInput.click();
|
| 861 |
fileInput.onchange = e => handleFile(e.target.files[0]);
|
|
|
|
|
|
|
| 862 |
dropZone.ondragover = e => { e.preventDefault(); dropZone.style.borderColor = 'var(--accent)'; };
|
| 863 |
dropZone.ondragleave = () => { dropZone.style.borderColor = ''; };
|
| 864 |
dropZone.ondrop = e => {
|
|
@@ -922,9 +1049,9 @@ const MODEL_INFO = {
|
|
| 922 |
modelSelect.onchange = () => {
|
| 923 |
const info = MODEL_INFO[modelSelect.value];
|
| 924 |
if (!info) return;
|
| 925 |
-
modelInfoBox.innerHTML
|
| 926 |
-
modelInfoBox.style.background
|
| 927 |
-
modelInfoBox.style.border
|
| 928 |
};
|
| 929 |
|
| 930 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -1083,7 +1210,7 @@ const dotGnd = document.getElementById('dot-gnd');
|
|
| 1083 |
runBtn.onclick = async () => {
|
| 1084 |
if (!currentFile) { alert('Please upload an image into the Input Node.'); return; }
|
| 1085 |
const promptStr = promptInput.value.trim();
|
| 1086 |
-
if (!promptStr)
|
| 1087 |
|
| 1088 |
runBtn.disabled = true;
|
| 1089 |
btnLoader.style.display = 'inline-block';
|
|
|
|
| 165 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-4B model not loaded.'})}\n\n"
|
| 166 |
yield "data: [DONE]\n\n"
|
| 167 |
return
|
|
|
|
| 168 |
messages = [{"role": "user", "content": [
|
| 169 |
{"type": "image", "image": image},
|
| 170 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 196 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-2B model not loaded.'})}\n\n"
|
| 197 |
yield "data: [DONE]\n\n"
|
| 198 |
return
|
|
|
|
| 199 |
messages = [{"role": "user", "content": [
|
| 200 |
{"type": "image", "image": image},
|
| 201 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 227 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3-VL model not loaded.'})}\n\n"
|
| 228 |
yield "data: [DONE]\n\n"
|
| 229 |
return
|
|
|
|
| 230 |
messages = [{"role": "user", "content": [
|
| 231 |
{"type": "image", "image": image},
|
| 232 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 258 |
yield f"data: {json.dumps({'chunk': '[Error] LFM-450M model not loaded.'})}\n\n"
|
| 259 |
yield "data: [DONE]\n\n"
|
| 260 |
return
|
|
|
|
| 261 |
conversation = [{"role": "user", "content": [
|
| 262 |
{"type": "image", "image": image},
|
| 263 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 286 |
yield f"data: {json.dumps({'chunk': '[Error] LFM-1.6B model not loaded.'})}\n\n"
|
| 287 |
yield "data: [DONE]\n\n"
|
| 288 |
return
|
|
|
|
| 289 |
conversation = [{"role": "user", "content": [
|
| 290 |
{"type": "image", "image": image},
|
| 291 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 314 |
yield f"data: {json.dumps({'chunk': '[Error] Qwen3.5-2B-Unredacted-MAX model not loaded.'})}\n\n"
|
| 315 |
yield "data: [DONE]\n\n"
|
| 316 |
return
|
|
|
|
| 317 |
messages = [{"role": "user", "content": [
|
| 318 |
{"type": "image", "image": image},
|
| 319 |
{"type": "text", "text": full_prompt},
|
|
|
|
| 436 |
position: relative;
|
| 437 |
width: 1560px;
|
| 438 |
min-height: calc(100vh - 50px);
|
| 439 |
+
height: 1080px;
|
| 440 |
margin: 0 auto;
|
| 441 |
}
|
| 442 |
|
|
|
|
| 472 |
.node:hover {
|
| 473 |
box-shadow: 0 10px 34px rgba(0,0,0,0.5), 0 0 0 1px rgba(124,106,247,0.3);
|
| 474 |
}
|
| 475 |
+
.node.fixed-height { height: 420px; }
|
| 476 |
|
| 477 |
.node-header {
|
| 478 |
background: var(--node-header);
|
|
|
|
| 518 |
|
| 519 |
input[type="file"] { display: none; }
|
| 520 |
|
| 521 |
+
/* ββ Image Upload Zone ββ */
|
| 522 |
+
.upload-area {
|
| 523 |
+
position: relative;
|
| 524 |
+
display: flex; flex-direction: column; gap: 0;
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
.file-upload {
|
| 528 |
border: 1.5px dashed var(--node-border);
|
| 529 |
+
border-radius: 8px; padding: 18px 12px;
|
| 530 |
text-align: center; cursor: pointer;
|
| 531 |
font-size: 12px; color: var(--muted);
|
| 532 |
transition: border-color 0.2s, background 0.2s;
|
| 533 |
background: rgba(255,255,255,0.01);
|
| 534 |
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
| 535 |
}
|
| 536 |
+
.file-upload:hover {
|
| 537 |
+
border-color: var(--accent);
|
| 538 |
+
background: rgba(124,106,247,0.04);
|
| 539 |
+
}
|
| 540 |
.file-upload svg { opacity: 0.5; transition: opacity 0.2s; }
|
| 541 |
.file-upload:hover svg { opacity: 0.9; }
|
| 542 |
|
| 543 |
+
/* ββ Preview wrapper ββ */
|
| 544 |
+
.preview-wrap {
|
| 545 |
+
display: none;
|
| 546 |
+
position: relative;
|
| 547 |
+
border-radius: 8px;
|
| 548 |
+
overflow: hidden;
|
| 549 |
+
border: 1px solid var(--node-border);
|
| 550 |
+
background: #000;
|
| 551 |
+
}
|
| 552 |
+
.preview-wrap.visible { display: block; }
|
| 553 |
+
|
| 554 |
.img-preview {
|
| 555 |
+
width: 100%;
|
| 556 |
+
height: 210px;
|
| 557 |
+
object-fit: contain;
|
| 558 |
+
display: block;
|
| 559 |
+
}
|
| 560 |
+
|
| 561 |
+
/* ββ Clear button overlaid on preview ββ */
|
| 562 |
+
.clear-btn {
|
| 563 |
+
position: absolute;
|
| 564 |
+
top: 7px; right: 7px;
|
| 565 |
+
width: 28px; height: 28px;
|
| 566 |
+
border-radius: 50%;
|
| 567 |
+
background: rgba(13,13,15,0.82);
|
| 568 |
border: 1px solid var(--node-border);
|
| 569 |
+
color: var(--accent3);
|
| 570 |
+
cursor: pointer;
|
| 571 |
+
display: flex; align-items: center; justify-content: center;
|
| 572 |
+
transition: background 0.18s, border-color 0.18s, transform 0.12s;
|
| 573 |
+
z-index: 20;
|
| 574 |
+
backdrop-filter: blur(6px);
|
| 575 |
+
}
|
| 576 |
+
.clear-btn:hover {
|
| 577 |
+
background: rgba(255,107,107,0.18);
|
| 578 |
+
border-color: var(--accent3);
|
| 579 |
+
transform: scale(1.08);
|
| 580 |
+
}
|
| 581 |
+
.clear-btn:active { transform: scale(0.96); }
|
| 582 |
+
.clear-btn svg { pointer-events: none; }
|
| 583 |
+
|
| 584 |
+
/* ββ Image filename chip ββ */
|
| 585 |
+
.img-chip {
|
| 586 |
+
display: none;
|
| 587 |
+
align-items: center; gap: 7px;
|
| 588 |
+
background: rgba(124,106,247,0.08);
|
| 589 |
+
border: 1px solid rgba(124,106,247,0.22);
|
| 590 |
+
border-radius: 6px;
|
| 591 |
+
padding: 5px 10px;
|
| 592 |
+
font-size: 10px; color: var(--muted);
|
| 593 |
+
overflow: hidden;
|
| 594 |
+
}
|
| 595 |
+
.img-chip.visible { display: flex; }
|
| 596 |
+
.img-chip .chip-dot {
|
| 597 |
+
width: 6px; height: 6px; border-radius: 50%;
|
| 598 |
+
background: var(--accent2); flex-shrink: 0;
|
| 599 |
+
box-shadow: 0 0 5px var(--accent2);
|
| 600 |
+
}
|
| 601 |
+
.img-chip .chip-name {
|
| 602 |
+
overflow: hidden; text-overflow: ellipsis;
|
| 603 |
+
white-space: nowrap; flex: 1;
|
| 604 |
+
color: var(--text);
|
| 605 |
}
|
| 606 |
+
.img-chip .chip-size { color: var(--muted); flex-shrink: 0; }
|
| 607 |
|
| 608 |
select, textarea {
|
| 609 |
width: 100%;
|
|
|
|
| 630 |
}
|
| 631 |
button.run-btn:hover { opacity: 0.9; }
|
| 632 |
button.run-btn:active { transform: scale(0.98); }
|
| 633 |
+
button.run-btn:disabled {
|
| 634 |
+
background: var(--node-border); cursor: not-allowed; color: #555;
|
| 635 |
+
}
|
| 636 |
|
| 637 |
.output-box {
|
| 638 |
background: rgba(0,0,0,0.4);
|
|
|
|
| 645 |
font-family: 'JetBrains Mono', monospace;
|
| 646 |
}
|
| 647 |
|
| 648 |
+
/* ββ Grounding ββ */
|
| 649 |
.ground-canvas-wrap {
|
| 650 |
position: relative; flex: 1;
|
| 651 |
border: 1px solid var(--node-border);
|
|
|
|
| 728 |
<div class="node-body">
|
| 729 |
<div>
|
| 730 |
<label>Upload Image</label>
|
| 731 |
+
|
| 732 |
+
<!-- Drop zone (shown when no image) -->
|
| 733 |
<div class="file-upload" id="dropZone">
|
| 734 |
<svg width="34" height="34" viewBox="0 0 24 24" fill="none"
|
| 735 |
stroke="#7c6af7" stroke-width="1.5"
|
|
|
|
| 741 |
<span>Click or drop image here</span>
|
| 742 |
<input type="file" id="fileInput" accept="image/*">
|
| 743 |
</div>
|
| 744 |
+
|
| 745 |
+
<!-- Preview (shown when image loaded) -->
|
| 746 |
+
<div class="preview-wrap" id="previewWrap">
|
| 747 |
+
<img id="imgPreview" class="img-preview" />
|
| 748 |
+
<!-- Clear button -->
|
| 749 |
+
<button class="clear-btn" id="clearBtn" title="Remove image">
|
| 750 |
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
|
| 751 |
+
stroke="currentColor" stroke-width="2.5"
|
| 752 |
+
stroke-linecap="round" stroke-linejoin="round">
|
| 753 |
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
| 754 |
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
| 755 |
+
</svg>
|
| 756 |
+
</button>
|
| 757 |
+
</div>
|
| 758 |
+
|
| 759 |
+
<!-- Filename chip -->
|
| 760 |
+
<div class="img-chip" id="imgChip" style="margin-top:7px;">
|
| 761 |
+
<span class="chip-dot"></span>
|
| 762 |
+
<span class="chip-name" id="chipName">β</span>
|
| 763 |
+
<span class="chip-size" id="chipSize"></span>
|
| 764 |
+
</div>
|
| 765 |
</div>
|
| 766 |
</div>
|
| 767 |
<div class="port out" id="port-img-out" style="top:50%;transform:translateY(-50%);"></div>
|
| 768 |
</div>
|
| 769 |
|
| 770 |
<!-- βββ ID 02 : Model Selector βββ -->
|
| 771 |
+
<div class="node fixed-height" id="node-model" style="left:48px; top:502px;">
|
| 772 |
<div class="node-header">
|
| 773 |
<span><span class="status-dot" id="dot-model"></span>Model Selector</span>
|
| 774 |
<span class="id">ID: 02</span>
|
|
|
|
| 840 |
</div>
|
| 841 |
|
| 842 |
<!-- βββ ID 05 : Grounding Visualiser βββ -->
|
| 843 |
+
<div class="node fixed-height" id="node-gnd" style="left:932px; top:502px;">
|
| 844 |
<div class="port in" id="port-gnd-in" style="top:50%;transform:translateY(-50%);"></div>
|
| 845 |
<div class="node-header">
|
| 846 |
<span><span class="status-dot" id="dot-gnd"></span>View Grounding</span>
|
|
|
|
| 884 |
|
| 885 |
function updateWires() {
|
| 886 |
const wires = [
|
| 887 |
+
['wire-img-task', 'port-img-out', 'port-task-in'],
|
| 888 |
+
['wire-model-task', 'port-model-out', 'port-task-in'],
|
| 889 |
+
['wire-task-out', 'port-task-out', 'port-out-in'],
|
| 890 |
+
['wire-task-gnd', 'port-task-out', 'port-gnd-in'],
|
| 891 |
];
|
| 892 |
for (const [id, from, to] of wires) {
|
| 893 |
const el = document.getElementById(id);
|
|
|
|
| 924 |
requestAnimationFrame(updateWires);
|
| 925 |
|
| 926 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 927 |
+
// FILE UPLOAD + CLEAR
|
| 928 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 929 |
let currentFile = null;
|
| 930 |
+
|
| 931 |
const dropZone = document.getElementById('dropZone');
|
| 932 |
const fileInput = document.getElementById('fileInput');
|
| 933 |
+
const previewWrap= document.getElementById('previewWrap');
|
| 934 |
const imgPreview = document.getElementById('imgPreview');
|
| 935 |
+
const clearBtn = document.getElementById('clearBtn');
|
| 936 |
+
const imgChip = document.getElementById('imgChip');
|
| 937 |
+
const chipName = document.getElementById('chipName');
|
| 938 |
+
const chipSize = document.getElementById('chipSize');
|
| 939 |
const dotImg = document.getElementById('dot-img');
|
| 940 |
|
| 941 |
+
function formatBytes(bytes) {
|
| 942 |
+
if (bytes < 1024) return bytes + ' B';
|
| 943 |
+
if (bytes < 1024*1024) return (bytes/1024).toFixed(1) + ' KB';
|
| 944 |
+
return (bytes/(1024*1024)).toFixed(1) + ' MB';
|
| 945 |
+
}
|
| 946 |
+
|
| 947 |
function handleFile(file) {
|
| 948 |
if (!file || !file.type.startsWith('image/')) return;
|
| 949 |
currentFile = file;
|
| 950 |
+
|
| 951 |
+
// preview
|
| 952 |
imgPreview.src = URL.createObjectURL(file);
|
| 953 |
+
previewWrap.classList.add('visible');
|
| 954 |
+
dropZone.style.display = 'none';
|
| 955 |
+
|
| 956 |
+
// chip
|
| 957 |
+
chipName.textContent = file.name;
|
| 958 |
+
chipSize.textContent = formatBytes(file.size);
|
| 959 |
+
imgChip.classList.add('visible');
|
| 960 |
+
|
| 961 |
dotImg.classList.add('active');
|
| 962 |
requestAnimationFrame(updateWires);
|
| 963 |
}
|
| 964 |
|
| 965 |
+
function clearImage() {
|
| 966 |
+
currentFile = null;
|
| 967 |
+
|
| 968 |
+
// reset preview
|
| 969 |
+
imgPreview.src = '';
|
| 970 |
+
previewWrap.classList.remove('visible');
|
| 971 |
+
dropZone.style.display = '';
|
| 972 |
+
|
| 973 |
+
// reset chip
|
| 974 |
+
imgChip.classList.remove('visible');
|
| 975 |
+
chipName.textContent = 'β';
|
| 976 |
+
chipSize.textContent = '';
|
| 977 |
+
|
| 978 |
+
// reset file input so same file can be re-selected
|
| 979 |
+
fileInput.value = '';
|
| 980 |
+
|
| 981 |
+
dotImg.classList.remove('active');
|
| 982 |
+
requestAnimationFrame(updateWires);
|
| 983 |
+
}
|
| 984 |
+
|
| 985 |
dropZone.onclick = () => fileInput.click();
|
| 986 |
fileInput.onchange = e => handleFile(e.target.files[0]);
|
| 987 |
+
clearBtn.onclick = (e) => { e.stopPropagation(); clearImage(); };
|
| 988 |
+
|
| 989 |
dropZone.ondragover = e => { e.preventDefault(); dropZone.style.borderColor = 'var(--accent)'; };
|
| 990 |
dropZone.ondragleave = () => { dropZone.style.borderColor = ''; };
|
| 991 |
dropZone.ondrop = e => {
|
|
|
|
| 1049 |
modelSelect.onchange = () => {
|
| 1050 |
const info = MODEL_INFO[modelSelect.value];
|
| 1051 |
if (!info) return;
|
| 1052 |
+
modelInfoBox.innerHTML = info.html;
|
| 1053 |
+
modelInfoBox.style.background = info.bg;
|
| 1054 |
+
modelInfoBox.style.border = `1px solid ${info.border}`;
|
| 1055 |
};
|
| 1056 |
|
| 1057 |
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 1210 |
runBtn.onclick = async () => {
|
| 1211 |
if (!currentFile) { alert('Please upload an image into the Input Node.'); return; }
|
| 1212 |
const promptStr = promptInput.value.trim();
|
| 1213 |
+
if (!promptStr) { alert('Please enter a prompt directive.'); return; }
|
| 1214 |
|
| 1215 |
runBtn.disabled = true;
|
| 1216 |
btnLoader.style.display = 'inline-block';
|