Geraldine commited on
Commit
5f0d254
·
verified ·
1 Parent(s): ef0156e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -9
app.py CHANGED
@@ -397,6 +397,9 @@ def b64_to_pil(b64_str):
397
  if not b64_str:
398
  return None
399
  try:
 
 
 
400
  if b64_str.startswith("data:"):
401
  _, data = b64_str.split(",", 1)
402
  else:
@@ -700,6 +703,21 @@ footer{display:none!important}
700
  .upload-click-area svg{width:86px;height:86px;max-width:100%;flex-shrink:0}
701
  .upload-main-text{color:#a1a1aa;font-size:14px;font-weight:600;margin-top:4px}
702
  .upload-sub-text{color:#71717a;font-size:12px}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
703
  .single-preview-wrap{
704
  width:100%;height:100%;display:none;align-items:center;justify-content:center;padding:16px;
705
  overflow:hidden;
@@ -937,13 +955,15 @@ function init() {
937
  const previewImg = document.getElementById('single-preview-img');
938
  const btnUpload = document.getElementById('preview-upload-btn');
939
  const btnClear = document.getElementById('preview-clear-btn');
 
 
940
  const promptInput = document.getElementById('custom-query-input');
941
  const promptPanel = document.getElementById('prompt-panel');
942
  const promptTabsBar = document.getElementById('prompt-tabs-bar');
943
  const runBtnEl = document.getElementById('custom-run-btn');
944
  const outputArea = document.getElementById('custom-output-textarea');
945
  const imgStatus = document.getElementById('sb-image-status');
946
- if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
947
  setTimeout(init, 250);
948
  return;
949
  }
@@ -1034,8 +1054,8 @@ function init() {
1034
  return false;
1035
  }
1036
  function syncImageToGradio() {
1037
- setGradioValue('hidden-image-b64', imageState ? imageState.b64 : '');
1038
- const txt = imageState ? '1 image uploaded' : 'No image uploaded';
1039
  if (imgStatus) imgStatus.textContent = txt;
1040
  }
1041
  function syncPromptToGradio() {
@@ -1045,11 +1065,12 @@ function init() {
1045
  function syncModelToGradio(name) {
1046
  setGradioValue('hidden-model-name', name);
1047
  }
1048
- function setPreview(b64, name) {
1049
- imageState = {b64, name: name || 'image'};
1050
- previewImg.src = b64;
1051
  previewWrap.style.display = 'flex';
1052
  if (uploadPrompt) uploadPrompt.style.display = 'none';
 
1053
  syncImageToGradio();
1054
  }
1055
  window.__setPreview = setPreview;
@@ -1068,14 +1089,47 @@ function init() {
1068
  return;
1069
  }
1070
  const reader = new FileReader();
1071
- reader.onload = (e) => setPreview(e.target.result, file.name);
1072
  reader.readAsDataURL(file);
1073
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1074
  fileInput.addEventListener('change', (e) => {
1075
  const file = e.target.files && e.target.files[0] ? e.target.files[0] : null;
1076
  if (file) processFile(file);
1077
  e.target.value = '';
1078
  });
 
 
 
 
 
 
 
1079
  if (uploadClick) uploadClick.addEventListener('click', () => fileInput.click());
1080
  if (btnUpload) btnUpload.addEventListener('click', () => fileInput.click());
1081
  if (btnClear) btnClear.addEventListener('click', clearPreview);
@@ -1252,7 +1306,7 @@ function init() {
1252
  try {
1253
  const data = JSON.parse(raw);
1254
  if (data.status === 'ok') {
1255
- if (data.image) setPreview(data.image, data.name || 'example.jpg');
1256
  if (data.query) {
1257
  promptInput.value = data.query;
1258
  syncPromptToGradio();
@@ -1456,6 +1510,10 @@ with gr.Blocks() as demo:
1456
  {UPLOAD_PREVIEW_SVG}
1457
  <span class="upload-main-text">Click or drag an image here</span>
1458
  <span class="upload-sub-text">Upload one document, page, receipt, screenshot, or scene image for OCR and vision tasks</span>
 
 
 
 
1459
  </div>
1460
  </div>
1461
  <input id="custom-file-input" type="file" accept="image/*" style="display:none;" />
@@ -1470,7 +1528,7 @@ with gr.Blocks() as demo:
1470
  </div>
1471
  </div>
1472
  <div class="hint-bar">
1473
- <b>Upload:</b> Click or drag to add an image &nbsp;&middot;&nbsp;
1474
  <b>Model:</b> Switch model tabs from the header &nbsp;&middot;&nbsp;
1475
  <kbd>Clear</kbd> removes the current image
1476
  </div>
 
397
  if not b64_str:
398
  return None
399
  try:
400
+ if is_image_url(b64_str):
401
+ with urlopen(b64_str, timeout=15) as response:
402
+ return Image.open(BytesIO(response.read())).convert("RGB")
403
  if b64_str.startswith("data:"):
404
  _, data = b64_str.split(",", 1)
405
  else:
 
703
  .upload-click-area svg{width:86px;height:86px;max-width:100%;flex-shrink:0}
704
  .upload-main-text{color:#a1a1aa;font-size:14px;font-weight:600;margin-top:4px}
705
  .upload-sub-text{color:#71717a;font-size:12px}
706
+ .upload-url-box{
707
+ margin-top:14px;display:flex;gap:8px;width:min(520px,100%);max-width:100%;
708
+ align-items:center;justify-content:center;flex-wrap:wrap;
709
+ }
710
+ .upload-url-input{
711
+ flex:1;min-width:240px;background:#09090b;border:1px solid #27272a;border-radius:10px;
712
+ color:#e4e4e7;padding:10px 12px;font-size:13px;outline:none;
713
+ }
714
+ .upload-url-input:focus{border-color:#ADFF2F;box-shadow:0 0 0 3px rgba(173,255,47,.14)}
715
+ .upload-url-input::placeholder{color:#52525b}
716
+ .upload-url-btn{
717
+ min-width:96px;height:40px;padding:0 14px;background:rgba(173,255,47,.12);border:1px solid rgba(173,255,47,.28);
718
+ border-radius:10px;cursor:pointer;color:#D6FF8C;font-size:12px;font-weight:700;transition:all .15s ease;
719
+ }
720
+ .upload-url-btn:hover{background:#ADFF2F;border-color:#ADFF2F;color:#111}
721
  .single-preview-wrap{
722
  width:100%;height:100%;display:none;align-items:center;justify-content:center;padding:16px;
723
  overflow:hidden;
 
955
  const previewImg = document.getElementById('single-preview-img');
956
  const btnUpload = document.getElementById('preview-upload-btn');
957
  const btnClear = document.getElementById('preview-clear-btn');
958
+ const urlInput = document.getElementById('image-url-input');
959
+ const urlBtn = document.getElementById('image-url-btn');
960
  const promptInput = document.getElementById('custom-query-input');
961
  const promptPanel = document.getElementById('prompt-panel');
962
  const promptTabsBar = document.getElementById('prompt-tabs-bar');
963
  const runBtnEl = document.getElementById('custom-run-btn');
964
  const outputArea = document.getElementById('custom-output-textarea');
965
  const imgStatus = document.getElementById('sb-image-status');
966
+ if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg || !urlInput || !urlBtn) {
967
  setTimeout(init, 250);
968
  return;
969
  }
 
1054
  return false;
1055
  }
1056
  function syncImageToGradio() {
1057
+ setGradioValue('hidden-image-b64', imageState ? imageState.value : '');
1058
+ const txt = imageState ? ('Image ready: ' + (imageState.name || 'image')) : 'No image uploaded';
1059
  if (imgStatus) imgStatus.textContent = txt;
1060
  }
1061
  function syncPromptToGradio() {
 
1065
  function syncModelToGradio(name) {
1066
  setGradioValue('hidden-model-name', name);
1067
  }
1068
+ function setPreview(src, name, value) {
1069
+ imageState = {src, name: name || 'image', value: value || src};
1070
+ previewImg.src = src;
1071
  previewWrap.style.display = 'flex';
1072
  if (uploadPrompt) uploadPrompt.style.display = 'none';
1073
+ if (urlInput) urlInput.value = '';
1074
  syncImageToGradio();
1075
  }
1076
  window.__setPreview = setPreview;
 
1089
  return;
1090
  }
1091
  const reader = new FileReader();
1092
+ reader.onload = (e) => setPreview(e.target.result, file.name, e.target.result);
1093
  reader.readAsDataURL(file);
1094
  }
1095
+ function applyImageUrl() {
1096
+ const raw = urlInput.value.trim();
1097
+ if (!raw) {
1098
+ showToast('Enter an image URL', 'warning');
1099
+ return;
1100
+ }
1101
+ let parsed;
1102
+ try {
1103
+ parsed = new URL(raw);
1104
+ } catch (e) {
1105
+ showToast('Invalid image URL', 'error');
1106
+ return;
1107
+ }
1108
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
1109
+ showToast('Only http/https image URLs are supported', 'error');
1110
+ return;
1111
+ }
1112
+ const testImg = new Image();
1113
+ testImg.onload = () => {
1114
+ const name = parsed.pathname.split('/').filter(Boolean).pop() || parsed.hostname;
1115
+ setPreview(raw, name, raw);
1116
+ showToast('Image URL loaded', 'info');
1117
+ };
1118
+ testImg.onerror = () => showToast('Could not load image from URL', 'error');
1119
+ testImg.src = raw;
1120
+ }
1121
  fileInput.addEventListener('change', (e) => {
1122
  const file = e.target.files && e.target.files[0] ? e.target.files[0] : null;
1123
  if (file) processFile(file);
1124
  e.target.value = '';
1125
  });
1126
+ urlBtn.addEventListener('click', applyImageUrl);
1127
+ urlInput.addEventListener('keydown', (e) => {
1128
+ if (e.key === 'Enter') {
1129
+ e.preventDefault();
1130
+ applyImageUrl();
1131
+ }
1132
+ });
1133
  if (uploadClick) uploadClick.addEventListener('click', () => fileInput.click());
1134
  if (btnUpload) btnUpload.addEventListener('click', () => fileInput.click());
1135
  if (btnClear) btnClear.addEventListener('click', clearPreview);
 
1306
  try {
1307
  const data = JSON.parse(raw);
1308
  if (data.status === 'ok') {
1309
+ if (data.image) setPreview(data.image, data.name || 'example.jpg', data.image);
1310
  if (data.query) {
1311
  promptInput.value = data.query;
1312
  syncPromptToGradio();
 
1510
  {UPLOAD_PREVIEW_SVG}
1511
  <span class="upload-main-text">Click or drag an image here</span>
1512
  <span class="upload-sub-text">Upload one document, page, receipt, screenshot, or scene image for OCR and vision tasks</span>
1513
+ <div class="upload-url-box">
1514
+ <input id="image-url-input" class="upload-url-input" type="url" placeholder="...or paste an image URL">
1515
+ <button id="image-url-btn" class="upload-url-btn" type="button">Use URL</button>
1516
+ </div>
1517
  </div>
1518
  </div>
1519
  <input id="custom-file-input" type="file" accept="image/*" style="display:none;" />
 
1528
  </div>
1529
  </div>
1530
  <div class="hint-bar">
1531
+ <b>Upload:</b> Click, drag, or paste an image URL &nbsp;&middot;&nbsp;
1532
  <b>Model:</b> Switch model tabs from the header &nbsp;&middot;&nbsp;
1533
  <kbd>Clear</kbd> removes the current image
1534
  </div>