Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -34,7 +34,7 @@ APP_DIR = os.getcwd()
|
|
| 34 |
SESSION_FILE = "/tmp/forge_session.json"
|
| 35 |
|
| 36 |
# Branding: fixed logo height
|
| 37 |
-
LOGO_HEIGHT_PX = int(os.getenv("FORGE_LOGO_PX",
|
| 38 |
|
| 39 |
# Settings live in a user cache dir (persists better than /tmp)
|
| 40 |
CONFIG_DIR = os.path.expanduser("~/.cache/forgecaptions")
|
|
@@ -707,7 +707,7 @@ def _render_header_html(px: int) -> str:
|
|
| 707 |
display: block;
|
| 708 |
max-width: 320px; /* cap very wide logos */
|
| 709 |
}}
|
| 710 |
-
@media (max-width:
|
| 711 |
.cf-logo {{ height: {max(48, int(px) - 8)}px; }}
|
| 712 |
}}
|
| 713 |
</style>
|
|
@@ -915,7 +915,7 @@ with gr.Blocks(css=BASE_CSS, title="ForgeCaptions") as demo:
|
|
| 915 |
gallery = gr.Gallery(
|
| 916 |
label="Results",
|
| 917 |
show_label=True,
|
| 918 |
-
columns=
|
| 919 |
elem_id="cfGal",
|
| 920 |
elem_classes=["cf-scroll"]
|
| 921 |
)
|
|
@@ -949,43 +949,111 @@ with gr.Blocks(css=BASE_CSS, title="ForgeCaptions") as demo:
|
|
| 949 |
export_txt_btn = gr.Button("Export captions as .txt (zip)")
|
| 950 |
txt_zip = gr.File(label="TXT zip", visible=False)
|
| 951 |
|
| 952 |
-
# ---- Robust scroll sync
|
| 953 |
-
gr.HTML("""
|
| 954 |
<script>
|
| 955 |
-
(
|
| 956 |
-
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
}
|
| 961 |
-
|
| 962 |
-
function
|
| 963 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 964 |
let lock = false;
|
| 965 |
-
|
| 966 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 967 |
a.addEventListener("scroll", onA, { passive: true });
|
| 968 |
b.addEventListener("scroll", onB, { passive: true });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
}
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 983 |
}
|
| 984 |
-
|
| 985 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 986 |
})();
|
| 987 |
</script>
|
| 988 |
-
|
| 989 |
|
| 990 |
# ---- Chunking logic
|
| 991 |
def _split_chunks(files, csize: int):
|
|
|
|
| 34 |
SESSION_FILE = "/tmp/forge_session.json"
|
| 35 |
|
| 36 |
# Branding: fixed logo height
|
| 37 |
+
LOGO_HEIGHT_PX = int(os.getenv("FORGE_LOGO_PX", 50))
|
| 38 |
|
| 39 |
# Settings live in a user cache dir (persists better than /tmp)
|
| 40 |
CONFIG_DIR = os.path.expanduser("~/.cache/forgecaptions")
|
|
|
|
| 707 |
display: block;
|
| 708 |
max-width: 320px; /* cap very wide logos */
|
| 709 |
}}
|
| 710 |
+
@media (max-width: 500px) {{
|
| 711 |
.cf-logo {{ height: {max(48, int(px) - 8)}px; }}
|
| 712 |
}}
|
| 713 |
</style>
|
|
|
|
| 915 |
gallery = gr.Gallery(
|
| 916 |
label="Results",
|
| 917 |
show_label=True,
|
| 918 |
+
columns=1,
|
| 919 |
elem_id="cfGal",
|
| 920 |
elem_classes=["cf-scroll"]
|
| 921 |
)
|
|
|
|
| 949 |
export_txt_btn = gr.Button("Export captions as .txt (zip)")
|
| 950 |
txt_zip = gr.File(label="TXT zip", visible=False)
|
| 951 |
|
|
|
|
|
|
|
| 952 |
<script>
|
| 953 |
+
(() => {
|
| 954 |
+
// ---- Config: your wrapper IDs
|
| 955 |
+
const GAL_WRAP_SEL = "#cfGal"; // wrapper around the gallery
|
| 956 |
+
const TABLE_WRAP_SEL = "#cfTableWrap"; // wrapper around your table
|
| 957 |
+
|
| 958 |
+
// ---- Helpers
|
| 959 |
+
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
| 960 |
+
|
| 961 |
+
function findGalleryHost() {
|
| 962 |
+
const wrap = document.querySelector(GAL_WRAP_SEL);
|
| 963 |
+
if (!wrap) return null;
|
| 964 |
+
// Prefer explicit gallery node if present; otherwise use wrapper
|
| 965 |
+
return wrap.querySelector('[data-testid="gallery"], [data-testid="image-gallery"]') || wrap;
|
| 966 |
}
|
| 967 |
+
|
| 968 |
+
function findTableHost() {
|
| 969 |
+
return document.querySelector(TABLE_WRAP_SEL);
|
| 970 |
+
}
|
| 971 |
+
|
| 972 |
+
function setMaxHeights(gal, tab) {
|
| 973 |
+
const targetH = clamp(tab.clientHeight || 520, 360, 520);
|
| 974 |
+
gal.style.maxHeight = targetH + "px";
|
| 975 |
+
gal.style.overflowY = "auto";
|
| 976 |
+
tab.style.maxHeight = targetH + "px";
|
| 977 |
+
tab.style.overflowY = "auto";
|
| 978 |
+
}
|
| 979 |
+
|
| 980 |
+
function attachScrollSync(a, b) {
|
| 981 |
+
if (!a || !b) return () => {};
|
| 982 |
let lock = false;
|
| 983 |
+
|
| 984 |
+
// Use scroll ratio so different scrollHeights stay aligned
|
| 985 |
+
const sync = (src, dst) => {
|
| 986 |
+
const maxSrc = Math.max(1, src.scrollHeight - src.clientHeight);
|
| 987 |
+
const r = src.scrollTop / maxSrc;
|
| 988 |
+
const maxDst = Math.max(1, dst.scrollHeight - dst.clientHeight);
|
| 989 |
+
const next = r * maxDst;
|
| 990 |
+
if (Math.abs(dst.scrollTop - next) > 1) {
|
| 991 |
+
dst.scrollTop = next;
|
| 992 |
+
}
|
| 993 |
+
};
|
| 994 |
+
|
| 995 |
+
const onA = () => { if (lock) return; lock = true; requestAnimationFrame(() => { sync(a, b); lock = false; }); };
|
| 996 |
+
const onB = () => { if (lock) return; lock = true; requestAnimationFrame(() => { sync(b, a); lock = false; }); };
|
| 997 |
+
|
| 998 |
a.addEventListener("scroll", onA, { passive: true });
|
| 999 |
b.addEventListener("scroll", onB, { passive: true });
|
| 1000 |
+
|
| 1001 |
+
// Return cleanup
|
| 1002 |
+
return () => {
|
| 1003 |
+
a.removeEventListener("scroll", onA);
|
| 1004 |
+
b.removeEventListener("scroll", onB);
|
| 1005 |
+
};
|
| 1006 |
}
|
| 1007 |
+
|
| 1008 |
+
let cleanupScroll = null;
|
| 1009 |
+
let resizeObs = null;
|
| 1010 |
+
|
| 1011 |
+
function wireUp() {
|
| 1012 |
+
const gal = findGalleryHost();
|
| 1013 |
+
const tab = findTableHost();
|
| 1014 |
+
if (!gal || !tab) return false;
|
| 1015 |
+
|
| 1016 |
+
// (Re)apply heights & listeners
|
| 1017 |
+
setMaxHeights(gal, tab);
|
| 1018 |
+
if (cleanupScroll) cleanupScroll();
|
| 1019 |
+
cleanupScroll = attachScrollSync(gal, tab);
|
| 1020 |
+
|
| 1021 |
+
// Keep heights in sync on resize/content changes
|
| 1022 |
+
if (resizeObs) resizeObs.disconnect();
|
| 1023 |
+
resizeObs = new ResizeObserver(() => setMaxHeights(gal, tab));
|
| 1024 |
+
resizeObs.observe(tab);
|
| 1025 |
+
resizeObs.observe(gal);
|
| 1026 |
+
|
| 1027 |
+
return true;
|
| 1028 |
+
}
|
| 1029 |
+
|
| 1030 |
+
// First attempt immediately (fast path)
|
| 1031 |
+
if (wireUp()) return;
|
| 1032 |
+
|
| 1033 |
+
// Observe DOM for late mounts / Gradio re-renders
|
| 1034 |
+
const mo = new MutationObserver(() => {
|
| 1035 |
+
if (wireUp()) {
|
| 1036 |
+
// Once wired, continue watching for *re-renders* to re-wire
|
| 1037 |
+
// If you prefer, remove this `disconnect()` to keep watching forever
|
| 1038 |
+
// but then you should also detect detach and clean up.
|
| 1039 |
+
// Here we keep observing to handle re-renders:
|
| 1040 |
}
|
| 1041 |
+
});
|
| 1042 |
+
|
| 1043 |
+
mo.observe(document.documentElement || document.body, {
|
| 1044 |
+
childList: true,
|
| 1045 |
+
subtree: true,
|
| 1046 |
+
});
|
| 1047 |
+
|
| 1048 |
+
// Optional: cleanup on page unload
|
| 1049 |
+
window.addEventListener("beforeunload", () => {
|
| 1050 |
+
mo.disconnect();
|
| 1051 |
+
if (resizeObs) resizeObs.disconnect();
|
| 1052 |
+
if (cleanupScroll) cleanupScroll();
|
| 1053 |
+
});
|
| 1054 |
})();
|
| 1055 |
</script>
|
| 1056 |
+
|
| 1057 |
|
| 1058 |
# ---- Chunking logic
|
| 1059 |
def _split_chunks(files, csize: int):
|