Spaces:
Sleeping
Sleeping
| """ | |
| UI helpers β shared functions across Gradio tabs. | |
| Keeps `app.py` focused on layout, not formatting logic. | |
| """ | |
| from __future__ import annotations | |
| from . import quota, workspace | |
| def get_status_bar() -> str: | |
| """Build the global status bar shown at the bottom of every tab. | |
| Markdown-formatted single line with quota + workspace info. | |
| """ | |
| state = workspace.get_state() | |
| parts = [ | |
| quota.format_status(), | |
| f"π Workspace: {workspace.current_size_mb():.1f} MB", | |
| f"π¦ Exports: {workspace.export_count()}", | |
| ] | |
| if state.model_used: | |
| parts.append(f"π§ Last: {state.model_used}") | |
| return " Β· ".join(parts) | |
| def get_asset_summary() -> str: | |
| """Multi-line summary of what's currently in `workspace/current/`. | |
| Used in the viewer panel to show pipeline progress. | |
| """ | |
| state = workspace.get_state() | |
| lines = [f"**Asset:** `{state.asset_name}`"] | |
| if state.face_count: | |
| lines.append(f"- Faces: {state.face_count:,}") | |
| lines.append(f"- Vertices: {state.vertex_count:,}") | |
| stages = [] | |
| if state.high_poly_glb: | |
| stages.append("β Generated") | |
| if state.cleaned_glb: | |
| stages.append("β Cleaned") | |
| if state.low_poly_glb: | |
| stages.append("β Decimated") | |
| if state.unwrapped_glb: | |
| stages.append("β UV unwrapped") | |
| if state.normal_dx_png or state.normal_gl_png: | |
| stages.append("β Normal baked") | |
| if state.albedo_png: | |
| stages.append("β Albedo baked") | |
| if state.orm_png or (state.roughness_png and state.metallic_png): | |
| stages.append("β PBR maps") | |
| if state.lod_glbs: | |
| stages.append(f"β LODs ({len(state.lod_glbs)})") | |
| if state.collision_glb: | |
| stages.append("β Collision") | |
| if state.rigged_glb or state.rigged_fbx: | |
| stages.append("β Rigged") | |
| if stages: | |
| lines.append("") | |
| lines.append("**Progress:**") | |
| for s in stages: | |
| lines.append(f"- {s}") | |
| else: | |
| lines.append("") | |
| lines.append("*No asset loaded yet. Start at the Generate tab.*") | |
| return "\n".join(lines) | |
| def get_viewer_model_path() -> str | None: | |
| """Pick the best GLB to show in the 3D viewer. | |
| Reads the filesystem directly so it works across the ZeroGPU subprocess | |
| boundary (the in-memory state written inside @spaces.GPU is invisible to | |
| the parent Gradio process). | |
| """ | |
| from .workspace import CURRENT | |
| # Order: most processed β least processed | |
| candidates = [ | |
| CURRENT / "rigged.glb", | |
| CURRENT / "scaled.glb", | |
| CURRENT / "pivoted.glb", | |
| CURRENT / "lods" / "LOD0.glb", | |
| CURRENT / "unwrapped.glb", | |
| CURRENT / "low_poly.glb", | |
| CURRENT / "cleaned.glb", | |
| CURRENT / "repaired.glb", | |
| CURRENT / "raw_gen.glb", | |
| CURRENT / "high_poly.glb", | |
| ] | |
| for path in candidates: | |
| if path.exists(): | |
| return str(path) | |
| return None | |
| def quota_warning(operation: str) -> str: | |
| """Generate a warning if an operation would exceed the daily quota. | |
| Returns an empty string if the operation fits comfortably. | |
| """ | |
| estimated = quota.estimate(operation) | |
| state = quota.get_state() | |
| remaining = state.remaining_seconds() | |
| if estimated > remaining: | |
| overage = estimated - remaining | |
| cost = overage * quota.OVERAGE_RATE_PER_SECOND | |
| return ( | |
| f"β οΈ **Quota warning:** This will use ~{estimated}s but you only " | |
| f"have {remaining:.0f}s left today. " | |
| f"Overage cost: ~${cost:.2f}" | |
| ) | |
| elif estimated > remaining * 0.5: | |
| return ( | |
| f"βΉοΈ Estimated GPU time: ~{estimated}s " | |
| f"({remaining:.0f}s remaining today)" | |
| ) | |
| return "" | |