update app [cleaned] ✅

#2
Files changed (1) hide show
  1. app.py +48 -66
app.py CHANGED
@@ -114,7 +114,6 @@ def infer_bbox_task(
114
  global loaded_adapters, current_adapter
115
  progress = gr.Progress(track_tqdm=True)
116
 
117
- # --- Prompt validation ---
118
  if not prompt or prompt.strip() == "":
119
  raise gr.Error("⚠ Prompt cannot be empty. Please enter a prompt describing the edit you want.")
120
 
@@ -136,7 +135,6 @@ def infer_bbox_task(
136
  if not boxes:
137
  raise gr.Error("Please draw at least one bounding box on the image.")
138
 
139
- # --- Object-Mover: exactly 2 boxes ---
140
  if adapter_choice == "Object-Mover":
141
  if len(boxes) != 2:
142
  raise gr.Error(
@@ -249,9 +247,11 @@ footer{display:none!important}
249
  font-size:13px;
250
  line-height:1.5;
251
  color:#e4e4e7;
 
 
252
  backdrop-filter:blur(16px);
253
  box-shadow:0 8px 32px rgba(0,0,0,.5),
254
- 0 0 0 1px rgba(255,255,255,.05);
255
  animation:toastSlideIn .35s cubic-bezier(.21,1.02,.73,1) forwards;
256
  transform:translateX(120%);
257
  opacity:0;
@@ -268,31 +268,32 @@ footer{display:none!important}
268
  to{transform:translateX(120%);opacity:0}
269
  }
270
  .toast-warning{
271
- background:rgba(30,27,15,.95);
272
- border-left:4px solid #F59E0B;
273
  }
274
  .toast-error{
275
- background:rgba(30,15,15,.95);
276
- border-left:4px solid #EF4444;
277
  }
278
  .toast-success{
279
- background:rgba(15,30,15,.95);
280
- border-left:4px solid #22C55E;
281
  }
282
  .toast-info{
283
- background:rgba(15,20,30,.95);
284
- border-left:4px solid #3B82F6;
285
  }
286
  .toast-icon{
287
  font-size:18px;
288
  line-height:1;
289
  flex-shrink:0;
290
  margin-top:1px;
 
291
  }
292
- .toast-warning .toast-icon{color:#F59E0B}
293
- .toast-error .toast-icon{color:#EF4444}
294
- .toast-success .toast-icon{color:#22C55E}
295
- .toast-info .toast-icon{color:#3B82F6}
296
  .toast-body{
297
  display:flex;
298
  flex-direction:column;
@@ -303,11 +304,12 @@ footer{display:none!important}
303
  .toast-title{
304
  font-weight:700;
305
  font-size:13px;
 
306
  }
307
- .toast-warning .toast-title{color:#FBBF24}
308
- .toast-error .toast-title{color:#F87171}
309
- .toast-success .toast-title{color:#4ADE80}
310
- .toast-info .toast-title{color:#60A5FA}
311
  .toast-msg{
312
  font-size:12.5px;
313
  color:#a1a1aa;
@@ -324,7 +326,7 @@ footer{display:none!important}
324
  padding:0 0 0 4px;
325
  transition:color .15s;
326
  }
327
- .toast-close:hover{color:#e4e4e7}
328
 
329
  /* ── Main Container ── */
330
  .app-shell{
@@ -519,13 +521,13 @@ footer{display:none!important}
519
  #bbox-count{
520
  position:absolute;top:12px;right:12px;
521
  background:rgba(24,24,27,.9);
522
- color:#FF0000;
523
  padding:4px 12px;
524
  font-family:'JetBrains Mono',monospace;
525
  font-size:12px;
526
  font-weight:600;
527
  border-radius:6px;
528
- border:1px solid rgba(255,0,0,.3);
529
  z-index:10;display:none;
530
  backdrop-filter:blur(8px);
531
  }
@@ -535,13 +537,13 @@ footer{display:none!important}
535
  position:absolute;bottom:12px;left:50%;
536
  transform:translateX(-50%);
537
  background:rgba(24,24,27,.92);
538
- color:#F59E0B;
539
  padding:6px 16px;
540
  font-family:'JetBrains Mono',monospace;
541
  font-size:12px;
542
  font-weight:600;
543
  border-radius:8px;
544
- border:1px solid rgba(245,158,11,.3);
545
  z-index:10;display:none;
546
  pointer-events:none;
547
  backdrop-filter:blur(8px);
@@ -604,12 +606,12 @@ footer{display:none!important}
604
  .hint-bar .hint-mover-tag{
605
  display:inline-block;
606
  padding:1px 8px;
607
- background:rgba(245,158,11,.15);
608
- border:1px solid rgba(245,158,11,.3);
609
  border-radius:4px;
610
  font-size:11px;
611
  font-weight:600;
612
- color:#F59E0B;
613
  }
614
 
615
  /* ── JSON Panel ── */
@@ -709,8 +711,8 @@ footer{display:none!important}
709
  }
710
  .modern-textarea::placeholder{color:#3f3f46}
711
  .modern-textarea.prompt-error{
712
- border-color:#EF4444!important;
713
- box-shadow:0 0 0 3px rgba(239,68,68,.2)!important;
714
  animation:promptShake .4s ease;
715
  }
716
  @keyframes promptShake{
@@ -798,15 +800,15 @@ footer{display:none!important}
798
  }
799
 
800
  .btn-run.mover-mode{
801
- background:linear-gradient(135deg,#F59E0B,#D97706)!important;
802
- box-shadow:0 4px 16px rgba(245,158,11,.3),
803
  inset 0 1px 0 rgba(255,255,255,.1)!important;
804
  color:#ffffff!important;
805
  -webkit-text-fill-color:#ffffff!important;
806
  }
807
  .btn-run.mover-mode:hover{
808
- background:linear-gradient(135deg,#FBBF24,#F59E0B)!important;
809
- box-shadow:0 6px 24px rgba(245,158,11,.45),
810
  inset 0 1px 0 rgba(255,255,255,.15)!important;
811
  color:#ffffff!important;
812
  -webkit-text-fill-color:#ffffff!important;
@@ -1108,10 +1110,6 @@ footer{display:none!important}
1108
  font-family:'JetBrains Mono',monospace;
1109
  font-size:12px;
1110
  }
1111
- .app-statusbar .sb-mode.sb-mode-mover{
1112
- background:rgba(245,158,11,.12);
1113
- color:#F59E0B;
1114
- }
1115
 
1116
  #gradio-run-btn{
1117
  position:absolute;
@@ -1306,9 +1304,9 @@ function initCanvasBbox() {
1306
  };
1307
 
1308
  const HINT_TEXTS = {
1309
- 'Object-Remover': '<b>Draw:</b> Click &amp; drag to create selection boxes &nbsp;\u00b7&nbsp; <b>Select:</b> Click a box to move or resize &nbsp;\u00b7&nbsp; <kbd>Delete</kbd> removes selected &nbsp;\u00b7&nbsp; <kbd>Clear</kbd> removes all &nbsp;\u00b7&nbsp; <kbd>Reset</kbd> removes image',
1310
- 'Design-Adder': '<b>Draw:</b> Click &amp; drag to create selection boxes &nbsp;\u00b7&nbsp; <b>Select:</b> Click a box to move or resize &nbsp;\u00b7&nbsp; <kbd>Delete</kbd> removes selected &nbsp;\u00b7&nbsp; <kbd>Clear</kbd> removes all &nbsp;\u00b7&nbsp; <kbd>Reset</kbd> removes image',
1311
- 'Object-Mover': '<span class="hint-mover-tag">MOVER</span> &nbsp; Draw <b>exactly 2</b> boxes: <b>Box 1 (SRC)</b> = the object to move &nbsp;\u00b7&nbsp; <b>Box 2 (DST)</b> = the target location &nbsp;\u00b7&nbsp; <kbd>Clear</kbd> to redraw &nbsp;\u00b7&nbsp; Only 2 boxes allowed'
1312
  };
1313
 
1314
  /* ── Toast System ── */
@@ -1434,13 +1432,13 @@ function initCanvasBbox() {
1434
  moverHint.style.display = 'block';
1435
  if (boxes.length === 0) {
1436
  moverHint.textContent = '\u25a0 Draw Box 1: Select the SOURCE object';
1437
- moverHint.style.color = '#F59E0B';
1438
  } else if (boxes.length === 1) {
1439
  moverHint.textContent = '\u25a1 Draw Box 2: Mark the TARGET location';
1440
- moverHint.style.color = '#3B82F6';
1441
  } else {
1442
  moverHint.textContent = '\u2714 Both boxes ready \u2014 Source + Target defined';
1443
- moverHint.style.color = '#22C55E';
1444
  }
1445
  }
1446
 
@@ -1470,19 +1468,19 @@ function initCanvasBbox() {
1470
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Object-Remover'];
1471
  if (runBtnEl) { runBtnEl.classList.remove('design-mode','mover-mode'); }
1472
  if (runBtnLabel) runBtnLabel.textContent = 'Remove Object';
1473
- if (statusMode) { statusMode.textContent = 'Object Remover'; statusMode.classList.remove('sb-mode-mover'); }
1474
  } else if (taskMode === 'Design-Adder') {
1475
  if (modeDesigner) modeDesigner.classList.add('active');
1476
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Design-Adder'];
1477
  if (runBtnEl) { runBtnEl.classList.remove('mover-mode'); runBtnEl.classList.add('design-mode'); }
1478
  if (runBtnLabel) runBtnLabel.textContent = 'Add Design';
1479
- if (statusMode) { statusMode.textContent = 'Design Adder'; statusMode.classList.remove('sb-mode-mover'); }
1480
  } else if (taskMode === 'Object-Mover') {
1481
  if (modeMover) modeMover.classList.add('active');
1482
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Object-Mover'];
1483
  if (runBtnEl) { runBtnEl.classList.remove('design-mode'); runBtnEl.classList.add('mover-mode'); }
1484
  if (runBtnLabel) runBtnLabel.textContent = 'Move Object';
1485
- if (statusMode) { statusMode.textContent = 'Object Mover'; statusMode.classList.add('sb-mode-mover'); }
1486
  if (boxes.length > 2) {
1487
  window.showToast(
1488
  'Object Mover requires exactly 2 boxes (Source + Target). You have ' + boxes.length + ' boxes. Please clear extra boxes before running.',
@@ -1518,13 +1516,6 @@ function initCanvasBbox() {
1518
  return '#'+(index+1);
1519
  }
1520
 
1521
- function getBoxLabelColor(index) {
1522
- if (window.__currentTaskMode === 'Object-Mover') {
1523
- return index === 0 ? '#F59E0B' : '#3B82F6';
1524
- }
1525
- return '#FF0000';
1526
- }
1527
-
1528
  function redraw(tempRect) {
1529
  ctx.clearRect(0,0,dispW,dispH);
1530
  if (!baseImg) {
@@ -1536,7 +1527,6 @@ function initCanvasBbox() {
1536
  boxes.forEach((b,i) => {
1537
  const p = n2px(b);
1538
  const lx=p.x1, ty=p.y1, w=p.x2-p.x1, h=p.y2-p.y1;
1539
- const boxColor = getBoxLabelColor(i);
1540
  if (i === selectedIdx) {
1541
  ctx.strokeStyle = SEL_STROKE;
1542
  ctx.lineWidth = RED_STROKE_WIDTH + 1;
@@ -1550,8 +1540,7 @@ function initCanvasBbox() {
1550
  ctx.setLineDash([]);
1551
 
1552
  const label = getBoxLabel(i);
1553
- const labelBg = boxColor;
1554
- ctx.fillStyle = labelBg;
1555
  ctx.font = 'bold 11px Inter,system-ui,sans-serif';
1556
  ctx.textAlign = 'left'; ctx.textBaseline = 'top';
1557
  const tw = ctx.measureText(label).width;
@@ -1627,18 +1616,16 @@ function initCanvasBbox() {
1627
  }
1628
 
1629
  function updateBadge() {
1630
- const isMover = window.__currentTaskMode === 'Object-Mover';
1631
  if (boxes.length > 0) {
1632
  badge.style.display = 'block';
 
1633
  if (isMover) {
1634
  badge.textContent = boxes.length + '/2 box' + (boxes.length>1?'es':'');
1635
- badge.style.color = boxes.length === 2 ? '#22C55E' : '#F59E0B';
1636
- badge.style.borderColor = boxes.length === 2 ? 'rgba(34,197,94,.3)' : 'rgba(245,158,11,.3)';
1637
  } else {
1638
  badge.textContent = boxes.length + ' box' + (boxes.length>1?'es':'');
1639
- badge.style.color = '#FF0000';
1640
- badge.style.borderColor = 'rgba(255,0,0,.3)';
1641
  }
 
 
1642
  } else {
1643
  badge.style.display = 'none';
1644
  }
@@ -1681,7 +1668,6 @@ function initCanvasBbox() {
1681
  e.preventDefault();
1682
  const {x, y} = canvasXY(e);
1683
  if (mode === 'draw') {
1684
- /* Enforce 2-box limit for Object-Mover before starting the drag */
1685
  if (window.__currentTaskMode === 'Object-Mover' && boxes.length >= 2) {
1686
  window.showToast(
1687
  'Object Mover allows exactly <b>2 boxes</b> (Source + Target). Clear existing boxes to redraw.',
@@ -1974,13 +1960,11 @@ function initCanvasBbox() {
1974
  const pInput = document.getElementById('custom-prompt-input');
1975
  const promptVal = pInput ? pInput.value.trim() : '';
1976
 
1977
- /* ── Validation: Image ── */
1978
  if (!baseImg) {
1979
  window.showToast('Please upload an image first before processing.', 'error', 'No Image');
1980
  return;
1981
  }
1982
 
1983
- /* ── Validation: Prompt ── */
1984
  if (!promptVal) {
1985
  window.showToast('Prompt cannot be empty. Please describe the edit you want to perform.', 'warning', 'Empty Prompt');
1986
  if (pInput) {
@@ -1991,13 +1975,11 @@ function initCanvasBbox() {
1991
  return;
1992
  }
1993
 
1994
- /* ── Validation: Boxes ── */
1995
  if (boxes.length === 0) {
1996
  window.showToast('Please draw at least one bounding box on the image.', 'warning', 'No Boxes');
1997
  return;
1998
  }
1999
 
2000
- /* ── Validation: Object-Mover requires exactly 2 boxes ── */
2001
  if (window.__currentTaskMode === 'Object-Mover') {
2002
  if (boxes.length < 2) {
2003
  window.showToast(
 
114
  global loaded_adapters, current_adapter
115
  progress = gr.Progress(track_tqdm=True)
116
 
 
117
  if not prompt or prompt.strip() == "":
118
  raise gr.Error("⚠ Prompt cannot be empty. Please enter a prompt describing the edit you want.")
119
 
 
135
  if not boxes:
136
  raise gr.Error("Please draw at least one bounding box on the image.")
137
 
 
138
  if adapter_choice == "Object-Mover":
139
  if len(boxes) != 2:
140
  raise gr.Error(
 
247
  font-size:13px;
248
  line-height:1.5;
249
  color:#e4e4e7;
250
+ background:rgba(30,18,28,.95);
251
+ border-left:4px solid #FF1493;
252
  backdrop-filter:blur(16px);
253
  box-shadow:0 8px 32px rgba(0,0,0,.5),
254
+ 0 0 0 1px rgba(255,20,147,.1);
255
  animation:toastSlideIn .35s cubic-bezier(.21,1.02,.73,1) forwards;
256
  transform:translateX(120%);
257
  opacity:0;
 
268
  to{transform:translateX(120%);opacity:0}
269
  }
270
  .toast-warning{
271
+ background:rgba(30,18,28,.95);
272
+ border-left:4px solid #FF69B4;
273
  }
274
  .toast-error{
275
+ background:rgba(30,15,20,.95);
276
+ border-left:4px solid #FF1493;
277
  }
278
  .toast-success{
279
+ background:rgba(20,22,28,.95);
280
+ border-left:4px solid #FFB6C1;
281
  }
282
  .toast-info{
283
+ background:rgba(25,18,30,.95);
284
+ border-left:4px solid #FF69B4;
285
  }
286
  .toast-icon{
287
  font-size:18px;
288
  line-height:1;
289
  flex-shrink:0;
290
  margin-top:1px;
291
+ color:#FF69B4;
292
  }
293
+ .toast-warning .toast-icon{color:#FF69B4}
294
+ .toast-error .toast-icon{color:#FF1493}
295
+ .toast-success .toast-icon{color:#FFB6C1}
296
+ .toast-info .toast-icon{color:#FF69B4}
297
  .toast-body{
298
  display:flex;
299
  flex-direction:column;
 
304
  .toast-title{
305
  font-weight:700;
306
  font-size:13px;
307
+ color:#FF69B4;
308
  }
309
+ .toast-warning .toast-title{color:#FF69B4}
310
+ .toast-error .toast-title{color:#FF1493}
311
+ .toast-success .toast-title{color:#FFB6C1}
312
+ .toast-info .toast-title{color:#FF69B4}
313
  .toast-msg{
314
  font-size:12.5px;
315
  color:#a1a1aa;
 
326
  padding:0 0 0 4px;
327
  transition:color .15s;
328
  }
329
+ .toast-close:hover{color:#FF69B4}
330
 
331
  /* ── Main Container ── */
332
  .app-shell{
 
521
  #bbox-count{
522
  position:absolute;top:12px;right:12px;
523
  background:rgba(24,24,27,.9);
524
+ color:#FF1493;
525
  padding:4px 12px;
526
  font-family:'JetBrains Mono',monospace;
527
  font-size:12px;
528
  font-weight:600;
529
  border-radius:6px;
530
+ border:1px solid rgba(255,20,147,.3);
531
  z-index:10;display:none;
532
  backdrop-filter:blur(8px);
533
  }
 
537
  position:absolute;bottom:12px;left:50%;
538
  transform:translateX(-50%);
539
  background:rgba(24,24,27,.92);
540
+ color:#FF69B4;
541
  padding:6px 16px;
542
  font-family:'JetBrains Mono',monospace;
543
  font-size:12px;
544
  font-weight:600;
545
  border-radius:8px;
546
+ border:1px solid rgba(255,20,147,.3);
547
  z-index:10;display:none;
548
  pointer-events:none;
549
  backdrop-filter:blur(8px);
 
606
  .hint-bar .hint-mover-tag{
607
  display:inline-block;
608
  padding:1px 8px;
609
+ background:rgba(255,20,147,.15);
610
+ border:1px solid rgba(255,20,147,.3);
611
  border-radius:4px;
612
  font-size:11px;
613
  font-weight:600;
614
+ color:#FF69B4;
615
  }
616
 
617
  /* ── JSON Panel ── */
 
711
  }
712
  .modern-textarea::placeholder{color:#3f3f46}
713
  .modern-textarea.prompt-error{
714
+ border-color:#FF1493!important;
715
+ box-shadow:0 0 0 3px rgba(255,20,147,.25)!important;
716
  animation:promptShake .4s ease;
717
  }
718
  @keyframes promptShake{
 
800
  }
801
 
802
  .btn-run.mover-mode{
803
+ background:linear-gradient(135deg,#C71585,#FF1493)!important;
804
+ box-shadow:0 4px 16px rgba(255,20,147,.3),
805
  inset 0 1px 0 rgba(255,255,255,.1)!important;
806
  color:#ffffff!important;
807
  -webkit-text-fill-color:#ffffff!important;
808
  }
809
  .btn-run.mover-mode:hover{
810
+ background:linear-gradient(135deg,#FF69B4,#C71585)!important;
811
+ box-shadow:0 6px 24px rgba(255,20,147,.45),
812
  inset 0 1px 0 rgba(255,255,255,.15)!important;
813
  color:#ffffff!important;
814
  -webkit-text-fill-color:#ffffff!important;
 
1110
  font-family:'JetBrains Mono',monospace;
1111
  font-size:12px;
1112
  }
 
 
 
 
1113
 
1114
  #gradio-run-btn{
1115
  position:absolute;
 
1304
  };
1305
 
1306
  const HINT_TEXTS = {
1307
+ 'Object-Remover': '<b>Draw:</b> Click &amp; drag to create selection boxes \u00b7 <b>Select:</b> Click a box to move or resize \u00b7 <kbd>Delete</kbd> removes selected \u00b7 <kbd>Clear</kbd> removes all \u00b7 <kbd>Reset</kbd> removes image',
1308
+ 'Design-Adder': '<b>Draw:</b> Click &amp; drag to create selection boxes \u00b7 <b>Select:</b> Click a box to move or resize \u00b7 <kbd>Delete</kbd> removes selected \u00b7 <kbd>Clear</kbd> removes all \u00b7 <kbd>Reset</kbd> removes image',
1309
+ 'Object-Mover': '<span class="hint-mover-tag">MOVER</span> Draw <b>exactly 2</b> boxes: <b>Box 1 (SRC)</b> = the object to move \u00b7 <b>Box 2 (DST)</b> = the target location \u00b7 <kbd>Clear</kbd> to redraw \u00b7 Only 2 boxes allowed'
1310
  };
1311
 
1312
  /* ── Toast System ── */
 
1432
  moverHint.style.display = 'block';
1433
  if (boxes.length === 0) {
1434
  moverHint.textContent = '\u25a0 Draw Box 1: Select the SOURCE object';
1435
+ moverHint.style.color = '#FF69B4';
1436
  } else if (boxes.length === 1) {
1437
  moverHint.textContent = '\u25a1 Draw Box 2: Mark the TARGET location';
1438
+ moverHint.style.color = '#FFB6C1';
1439
  } else {
1440
  moverHint.textContent = '\u2714 Both boxes ready \u2014 Source + Target defined';
1441
+ moverHint.style.color = '#FF69B4';
1442
  }
1443
  }
1444
 
 
1468
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Object-Remover'];
1469
  if (runBtnEl) { runBtnEl.classList.remove('design-mode','mover-mode'); }
1470
  if (runBtnLabel) runBtnLabel.textContent = 'Remove Object';
1471
+ if (statusMode) statusMode.textContent = 'Object Remover';
1472
  } else if (taskMode === 'Design-Adder') {
1473
  if (modeDesigner) modeDesigner.classList.add('active');
1474
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Design-Adder'];
1475
  if (runBtnEl) { runBtnEl.classList.remove('mover-mode'); runBtnEl.classList.add('design-mode'); }
1476
  if (runBtnLabel) runBtnLabel.textContent = 'Add Design';
1477
+ if (statusMode) statusMode.textContent = 'Design Adder';
1478
  } else if (taskMode === 'Object-Mover') {
1479
  if (modeMover) modeMover.classList.add('active');
1480
  if (promptInput) promptInput.value = DEFAULT_PROMPTS['Object-Mover'];
1481
  if (runBtnEl) { runBtnEl.classList.remove('design-mode'); runBtnEl.classList.add('mover-mode'); }
1482
  if (runBtnLabel) runBtnLabel.textContent = 'Move Object';
1483
+ if (statusMode) statusMode.textContent = 'Object Mover';
1484
  if (boxes.length > 2) {
1485
  window.showToast(
1486
  'Object Mover requires exactly 2 boxes (Source + Target). You have ' + boxes.length + ' boxes. Please clear extra boxes before running.',
 
1516
  return '#'+(index+1);
1517
  }
1518
 
 
 
 
 
 
 
 
1519
  function redraw(tempRect) {
1520
  ctx.clearRect(0,0,dispW,dispH);
1521
  if (!baseImg) {
 
1527
  boxes.forEach((b,i) => {
1528
  const p = n2px(b);
1529
  const lx=p.x1, ty=p.y1, w=p.x2-p.x1, h=p.y2-p.y1;
 
1530
  if (i === selectedIdx) {
1531
  ctx.strokeStyle = SEL_STROKE;
1532
  ctx.lineWidth = RED_STROKE_WIDTH + 1;
 
1540
  ctx.setLineDash([]);
1541
 
1542
  const label = getBoxLabel(i);
1543
+ ctx.fillStyle = '#FF0000';
 
1544
  ctx.font = 'bold 11px Inter,system-ui,sans-serif';
1545
  ctx.textAlign = 'left'; ctx.textBaseline = 'top';
1546
  const tw = ctx.measureText(label).width;
 
1616
  }
1617
 
1618
  function updateBadge() {
 
1619
  if (boxes.length > 0) {
1620
  badge.style.display = 'block';
1621
+ const isMover = window.__currentTaskMode === 'Object-Mover';
1622
  if (isMover) {
1623
  badge.textContent = boxes.length + '/2 box' + (boxes.length>1?'es':'');
 
 
1624
  } else {
1625
  badge.textContent = boxes.length + ' box' + (boxes.length>1?'es':'');
 
 
1626
  }
1627
+ badge.style.color = '#FF1493';
1628
+ badge.style.borderColor = 'rgba(255,20,147,.3)';
1629
  } else {
1630
  badge.style.display = 'none';
1631
  }
 
1668
  e.preventDefault();
1669
  const {x, y} = canvasXY(e);
1670
  if (mode === 'draw') {
 
1671
  if (window.__currentTaskMode === 'Object-Mover' && boxes.length >= 2) {
1672
  window.showToast(
1673
  'Object Mover allows exactly <b>2 boxes</b> (Source + Target). Clear existing boxes to redraw.',
 
1960
  const pInput = document.getElementById('custom-prompt-input');
1961
  const promptVal = pInput ? pInput.value.trim() : '';
1962
 
 
1963
  if (!baseImg) {
1964
  window.showToast('Please upload an image first before processing.', 'error', 'No Image');
1965
  return;
1966
  }
1967
 
 
1968
  if (!promptVal) {
1969
  window.showToast('Prompt cannot be empty. Please describe the edit you want to perform.', 'warning', 'Empty Prompt');
1970
  if (pInput) {
 
1975
  return;
1976
  }
1977
 
 
1978
  if (boxes.length === 0) {
1979
  window.showToast('Please draw at least one bounding box on the image.', 'warning', 'No Boxes');
1980
  return;
1981
  }
1982
 
 
1983
  if (window.__currentTaskMode === 'Object-Mover') {
1984
  if (boxes.length < 2) {
1985
  window.showToast(