Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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.
|
| 1038 |
-
const txt = imageState ? '
|
| 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(
|
| 1049 |
-
imageState = {
|
| 1050 |
-
previewImg.src =
|
| 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
|
| 1474 |
<b>Model:</b> Switch model tabs from the header ·
|
| 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 ·
|
| 1532 |
<b>Model:</b> Switch model tabs from the header ·
|
| 1533 |
<kbd>Clear</kbd> removes the current image
|
| 1534 |
</div>
|