Spaces:
Running on Zero
Running on Zero
feat(app): per-mode forms with media inputs, presets, advanced accordion
Browse files
app.py
CHANGED
|
@@ -86,17 +86,14 @@ _CUSTOM_CSS = """
|
|
| 86 |
|
| 87 |
|
| 88 |
def build_app() -> gr.Blocks:
|
| 89 |
-
with gr.Blocks(
|
| 90 |
-
theme=gr.themes.Soft(),
|
| 91 |
-
title="LTX 2.3 All-in-One",
|
| 92 |
-
css=_CUSTOM_CSS,
|
| 93 |
-
) as app:
|
| 94 |
gr.Markdown("# ⚡ LTX 2.3 All-in-One")
|
| 95 |
with gr.Row():
|
| 96 |
with gr.Column(scale=1, min_width=200):
|
| 97 |
_render_sidebar()
|
| 98 |
with gr.Column(scale=4):
|
| 99 |
-
_render_mode_panels()
|
|
|
|
| 100 |
return app
|
| 101 |
|
| 102 |
|
|
@@ -108,12 +105,60 @@ def _render_sidebar() -> None:
|
|
| 108 |
gr.Button("Unload all models", variant="secondary")
|
| 109 |
|
| 110 |
|
| 111 |
-
def _render_mode_panels() ->
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
for name, mode in modes.MODE_REGISTRY.items():
|
| 114 |
with gr.Tab(label=f"{mode.icon} {mode.label}"):
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
|
| 119 |
if __name__ == "__main__":
|
|
|
|
| 86 |
|
| 87 |
|
| 88 |
def build_app() -> gr.Blocks:
|
| 89 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="LTX 2.3 All-in-One", css=_CUSTOM_CSS) as app:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
gr.Markdown("# ⚡ LTX 2.3 All-in-One")
|
| 91 |
with gr.Row():
|
| 92 |
with gr.Column(scale=1, min_width=200):
|
| 93 |
_render_sidebar()
|
| 94 |
with gr.Column(scale=4):
|
| 95 |
+
handles = _render_mode_panels()
|
| 96 |
+
# Generate-handler wiring deferred to Task 23.
|
| 97 |
return app
|
| 98 |
|
| 99 |
|
|
|
|
| 105 |
gr.Button("Unload all models", variant="secondary")
|
| 106 |
|
| 107 |
|
| 108 |
+
def _render_mode_panels() -> dict[str, dict]:
|
| 109 |
+
"""Render one form per mode. Returns the component handles keyed by mode."""
|
| 110 |
+
handles: dict[str, dict] = {}
|
| 111 |
+
with gr.Tabs() as tabs:
|
| 112 |
for name, mode in modes.MODE_REGISTRY.items():
|
| 113 |
with gr.Tab(label=f"{mode.icon} {mode.label}"):
|
| 114 |
+
handles[name] = _render_one_mode(name)
|
| 115 |
+
return handles
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def _render_one_mode(name: str) -> dict:
|
| 119 |
+
"""Render a per-mode form. Returns component handles for the generate handler."""
|
| 120 |
+
mode = modes.MODE_REGISTRY[name]
|
| 121 |
+
handles: dict = {"mode": name}
|
| 122 |
+
|
| 123 |
+
with gr.Row():
|
| 124 |
+
with gr.Column(scale=2):
|
| 125 |
+
handles["prompt"] = gr.Textbox(label="Prompt", lines=4, placeholder="Describe the shot...")
|
| 126 |
+
|
| 127 |
+
# Mode-specific media inputs
|
| 128 |
+
if name == "i2v":
|
| 129 |
+
handles["image"] = gr.Image(label="Source image", type="filepath")
|
| 130 |
+
elif name == "a2v":
|
| 131 |
+
handles["audio"] = gr.Audio(label="Source audio", type="filepath")
|
| 132 |
+
elif name == "lipsync":
|
| 133 |
+
handles["image"] = gr.Image(label="Portrait", type="filepath")
|
| 134 |
+
handles["audio"] = gr.Audio(label="Speech audio", type="filepath")
|
| 135 |
+
elif name == "keyframe":
|
| 136 |
+
handles["first_frame"] = gr.Image(label="First frame", type="filepath")
|
| 137 |
+
handles["last_frame"] = gr.Image(label="Last frame", type="filepath")
|
| 138 |
+
elif name == "style":
|
| 139 |
+
handles["input_video"] = gr.Video(label="Source video")
|
| 140 |
+
|
| 141 |
+
handles["preset"] = ui.preset_bar()
|
| 142 |
+
with gr.Row():
|
| 143 |
+
handles["width"] = gr.Slider(256, 1280, value=512, step=32, label="Width")
|
| 144 |
+
handles["height"] = gr.Slider(256, 1280, value=768, step=32, label="Height")
|
| 145 |
+
with gr.Row():
|
| 146 |
+
handles["frames"] = gr.Slider(9, 121, value=81, step=8, label="Frames (8k+1)")
|
| 147 |
+
handles["fps"] = gr.Slider(8, 30, value=24, step=1, label="FPS")
|
| 148 |
+
handles["seed"] = gr.Number(label="Seed", value=42, precision=0)
|
| 149 |
+
|
| 150 |
+
with gr.Accordion("Advanced ▾", open=False):
|
| 151 |
+
handles["lora"] = ui.lora_chrome(name)
|
| 152 |
+
handles["negative_prompt"] = gr.Textbox(label="Negative prompt", lines=2)
|
| 153 |
+
|
| 154 |
+
handles["generate_btn"] = gr.Button("▶ Generate", variant="primary", size="lg")
|
| 155 |
+
|
| 156 |
+
with gr.Column(scale=2):
|
| 157 |
+
handles["status"] = ui.status_banner()
|
| 158 |
+
handles["video_out"] = gr.Video(label="Output", autoplay=True)
|
| 159 |
+
handles["history"] = gr.Markdown("")
|
| 160 |
+
|
| 161 |
+
return handles
|
| 162 |
|
| 163 |
|
| 164 |
if __name__ == "__main__":
|