from __future__ import annotations import inspect import os import re import socket from pathlib import Path import gradio as gr ROOT = Path(__file__).resolve().parent DIST_DIR = ROOT / "dist" ASSETS_DIR = DIST_DIR / "assets" PATCHED_DIR = ROOT / ".gradio_static" PORT = int(os.environ.get("GRADIO_PORT", "7899")) def get_free_port() -> int: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.bind(("127.0.0.1", 0)) return int(sock.getsockname()[1]) def file_url(path: Path) -> str: return f"/gradio_api/file={path.resolve()}" def patched_js_url(index_html: str) -> str: match = re.search(r'src="/assets/([^"]+\.js)"', index_html) if not match: raise RuntimeError("Could not find built JS bundle in dist/index.html") source_path = ASSETS_DIR / match.group(1) source = source_path.read_text() # The worker is referenced by an ABSOLUTE "/assets/worker-*.js" URL, which # cannot resolve inside the Gradio iframe — rewrite it to a file= URL. worker_match = re.search(r'worker-[^"]+\.js', source) if worker_match: worker_path = ASSETS_DIR / worker_match.group(0) source = source.replace(f'"/assets/{worker_path.name}"', f'"{file_url(worker_path)}"') source = source.replace('"logo.png"', f'"{file_url(DIST_DIR / "logo.png")}"') source = source.replace('"banner.png"', f'"{file_url(DIST_DIR / "banner.png")}"') source = source.replace('"yeSound.mp3"', f'"{file_url(DIST_DIR / "yeSound.mp3")}"') for _img in ("Image1.jpg", "Image2.jpg", "Image3.jpg"): source = source.replace(f'"{_img}"', f'"{file_url(DIST_DIR / _img)}"') # CRITICAL: lazily-imported chunks (e.g. the transformers.js bundle used by # =VISION()) are referenced RELATIVELY, like import("./transformers.web-*.js"), # resolved against this file's URL. So the patched bundle MUST live next to # those chunks (dist/assets/) — not in .gradio_static/ — or they 404 in the # iframe and on-device WebGPU inference silently fails. patched_path = ASSETS_DIR / (source_path.stem + ".patched.js") patched_path.write_text(source) return file_url(patched_path) def patched_index_url() -> str: index_path = DIST_DIR / "index.html" if not index_path.exists(): raise RuntimeError("Missing dist/index.html. Run `npm run build` first.") PATCHED_DIR.mkdir(exist_ok=True) index_html = index_path.read_text() # Never cache the entry document, so a rebuild is always picked up (the # hashed JS bundle name changes each build; a cached index.html would point # at a stale one). index_html = index_html.replace( "", '\n ', 1, ) index_html = re.sub( r'', f'', index_html, ) index_html = re.sub( r'href="/assets/([^"]+)"', lambda m: f'href="{file_url(ASSETS_DIR / m.group(1))}"', index_html, ) index_html = index_html.replace('href="/logo.png"', f'href="{file_url(DIST_DIR / "logo.png")}"') index_html = index_html.replace('src="logo.png"', f'src="{file_url(DIST_DIR / "logo.png")}"') index_html = index_html.replace('src="banner.png"', f'src="{file_url(DIST_DIR / "banner.png")}"') patched_index = PATCHED_DIR / "index.html" patched_index.write_text(index_html) return file_url(patched_index) def build_ui() -> gr.Blocks: css = """ html, body, #root, gradio-app, .gradio-container { width: 100vw !important; max-width: 100vw !important; height: 100%; margin: 0 !important; padding: 0 !important; overflow: hidden !important; background: #fff1a8 !important; } .gradio-container { max-width: none !important; } main.app, .main, .contain, .wrap, .block, .html-container, .html-container.padding, .prose, #component-0, #component-0 > div, .form { width: 100vw !important; max-width: 100vw !important; flex-basis: 100vw !important; align-self: stretch !important; height: 100% !important; min-height: 100% !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; transform: none !important; } .spreadsheet-host { position: relative; width: 100vw !important; max-width: 100vw !important; height: 100vh; height: 100dvh; margin: 0; padding: 0; overflow: hidden; background: white; } .spreadsheet-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; background: white; } .yellow-tint { position: absolute; inset: 0; pointer-events: none; z-index: 9999; background: rgba(255, 232, 83, 0.34); mix-blend-mode: multiply; } footer { display: none !important; } """ # Keep the iframe in normal layout flow. Hugging Face/Gradio measure the # document to size the outer Space iframe; hoisting a fixed-position child to # gives that measurer no stable height and can make the whole UI drift. head = """ """ # NOTE: Gradio 6 applies css/head at launch(); older Gradio applies them on # Blocks(). We support both so local verification does not depend on one # specific Gradio version. blocks_kwargs = {"title": "The Backrooms Spreadsheet", "fill_height": True} if "css" in inspect.signature(gr.Blocks).parameters: blocks_kwargs.update({"css": css, "head": head}) with gr.Blocks(**blocks_kwargs) as demo: gr.HTML( f"""
""" ) return demo, css, head if __name__ == "__main__": demo, css, head = build_ui() # On a Hugging Face Space, bind 0.0.0.0:7860 (HF sets SPACE_ID/PORT). Locally, # keep the old behaviour (localhost + a free port, or GRADIO_PORT if set). on_space = bool(os.environ.get("SPACE_ID")) if on_space: server_name = "0.0.0.0" server_port = int(os.environ.get("PORT", "7860")) else: server_name = "127.0.0.1" server_port = PORT if "GRADIO_PORT" in os.environ else get_free_port() print(f"Running The Backrooms Spreadsheet at http://{server_name}:{server_port}", flush=True) launch_kwargs = { "server_name": server_name, "server_port": server_port, "show_error": True, "allowed_paths": [str(DIST_DIR), str(PATCHED_DIR)], } launch_params = inspect.signature(demo.launch).parameters if "ssr_mode" in launch_params: launch_kwargs["ssr_mode"] = False # Gradio 6 enables SSR on Spaces, which shadows our custom iframe if "css" in launch_params: launch_kwargs["css"] = css # Gradio 6 ignores css/head on gr.Blocks(); they must be passed here if "head" in launch_params: launch_kwargs["head"] = head demo.launch(**launch_kwargs)