Spaces:
Paused
Paused
# ui/layout.py | |
# This file defines the Gradio UI layout for the goan application. | |
import gradio as gr | |
from gradio_modal import Modal | |
# Import workspace manager to get the default output folder path | |
from . import workspace as workspace_manager | |
def create_ui(): | |
""" | |
Creates the Gradio UI layout and returns a dictionary of all UI components. | |
This separation of layout from logic makes the main script cleaner. | |
""" | |
# Define a dictionary to hold all UI components, which will be returned. | |
components = {} | |
css = """ | |
#queue_df { font-size: 0.85rem; } | |
#queue_df th:nth-child(1), #queue_df td:nth-child(1) { width: 5%; } | |
#queue_df th:nth-child(2), #queue_df td:nth-child(2) { width: 10%; } | |
#queue_df th:nth-child(3), #queue_df td:nth-child(3) { width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;} | |
#queue_df th:nth-child(4), #queue_df td:nth-child(4) { width: 8%; } | |
#queue_df th:nth-child(5), #queue_df td:nth-child(5) { width: 8%; } | |
#queue_df th:nth-child(6), #queue_df td:nth-child(6) { width: 10%; text-align: center; } | |
#queue_df th:nth-child(7), #queue_df td:nth-child(7), | |
#queue_df th:nth-child(8), #queue_df td:nth-child(8), | |
#queue_df th:nth-child(9), #queue_df td:nth-child(9), | |
#queue_df th:nth-child(10), #queue_df td:nth-child(10) { width: 4%; cursor: pointer; text-align: center; } | |
#queue_df td:hover { background-color: #f0f0f0; } | |
.gradio-container { max-width: 95% !important; margin: auto !important; } | |
""" | |
block = gr.Blocks(css=css, title="goan").queue() | |
components['block'] = block | |
with block: | |
# The app_state dictionary holds all transient state for the UI | |
app_state = gr.State({ | |
"queue_state": {"queue": [], "next_id": 1, "processing": False, "editing_task_id": None}, | |
"last_completed_video_path": None | |
}) | |
components['app_state'] = app_state | |
gr.Markdown('# goan (Powered by FramePack)') | |
with Modal(visible=False) as metadata_modal: | |
components['metadata_modal'] = metadata_modal | |
gr.Markdown("Image has saved parameters. Overwrite current creative settings?") | |
with gr.Row(): | |
components['cancel_metadata_btn'] = gr.Button("No") | |
components['confirm_metadata_btn'] = gr.Button("Yes, Apply", variant="primary") | |
# --- Start of layout structure --- | |
# TOP ZONE: A row with two columns. | |
# Column 1 (scale=1): Image input and primary queue actions. | |
# Column 2 (scale=2): Prompting. | |
with gr.Row(): | |
# Column 1: Input Image & Primary Actions | |
with gr.Column(scale=1, min_width=300): | |
components['input_image_gallery_ui'] = gr.Gallery(type="pil", label="Input Image(s)", height=220) | |
components['add_task_button'] = gr.Button("Add to Queue", variant="secondary") | |
components['process_queue_button'] = gr.Button("▶️ Process Queue", variant="primary") | |
components['abort_task_button'] = gr.Button("⏹️ Abort", variant="stop", interactive=True) # default to active on startup in case service is still running | |
components['cancel_edit_task_button'] = gr.Button("Cancel Edit", visible=False, variant="secondary") | |
# Column 2: Prompts | |
with gr.Column(scale=2, min_width=600): | |
components['prompt_ui'] = gr.Textbox(label="Prompt", lines=10) | |
components['n_prompt_ui'] = gr.Textbox(label="Negative Prompt", lines=4) | |
# MIDDLE ZONE: Full-width task queue display and file operations | |
with gr.Group(): | |
gr.Markdown("## Task Queue") | |
components['queue_df_display_ui'] = gr.DataFrame(headers=["ID", "Status", "Prompt", "Length", "Steps", "Input", "↑", "↓", "✖", "✎"], datatype=["number","markdown","markdown","str","number","markdown","markdown","markdown","markdown","markdown"], col_count=(10,"fixed"), interactive=False, visible=False, elem_id="queue_df") | |
with gr.Row(): | |
components['save_queue_zip_b64_output'] = gr.Text(visible=False) | |
components['save_queue_button_ui'] = gr.DownloadButton("Save Queue", size="sm") | |
components['load_queue_button_ui'] = gr.UploadButton("Load Queue", file_types=[".zip"], size="sm") | |
components['clear_queue_button_ui'] = gr.Button("Clear Pending", size="sm", variant="stop") | |
# BOTTOM ZONE: A row split into two columns for Settings and Output | |
with gr.Row(equal_height=False): | |
# Column 1: All settings and parameters | |
with gr.Column(scale=1): | |
with gr.Row(): | |
components['total_second_length_ui'] = gr.Slider(label="Video Length (s)", minimum=0.1, maximum=120, value=5.0, step=0.1) | |
components['seed_ui'] = gr.Number(label="Seed", value=-1, precision=0) | |
with gr.Accordion("Advanced Settings", open=False): # keep default closed on startup | |
components['total_segments_display_ui'] = gr.Markdown("Calculated Total Segments: N/A") | |
components['preview_frequency_ui'] = gr.Slider(label="Preview Freq.", minimum=0, maximum=100, value=5, step=1) | |
components['segments_to_decode_csv_ui'] = gr.Textbox(label="Preview Segments CSV", value="") | |
with gr.Row(): | |
components['gs_ui'] = gr.Slider(label="Distilled CFG Start", minimum=1.0, maximum=32.0, value=10.0, step=0.01) | |
components['gs_schedule_shape_ui'] = gr.Radio(["Off", "Linear"], label="Variable CFG", value="Off") | |
components['gs_final_ui'] = gr.Slider(label="Distilled CFG End", minimum=1.0, maximum=32.0, value=10.0, step=0.01, interactive=False) | |
components['cfg_ui'] = gr.Slider(label="CFG (Real)", minimum=1.0, maximum=32.0, value=1.0, step=0.01) | |
components['steps_ui'] = gr.Slider(label="Steps", minimum=1, maximum=100, value=25, step=1) | |
components['rs_ui'] = gr.Slider(label="RS", minimum=0.0, maximum=32.0, value=0.0, step=0.01, visible=False) | |
with gr.Accordion("Debug Settings", open=False): # keep default closed on startup | |
components['use_teacache_ui'] = gr.Checkbox(label='Use TeaCache', value=True) | |
components['use_fp32_transformer_output_checkbox_ui'] = gr.Checkbox(label="Use FP32 Transformer Output", value=False) | |
components['gpu_memory_preservation_ui'] = gr.Slider(label="GPU Preserved (GB)", minimum=4, maximum=128, value=6.0, step=0.1) | |
components['mp4_crf_ui'] = gr.Slider(label="MP4 CRF", minimum=0, maximum=51, value=18, step=1) | |
components['latent_window_size_ui'] = gr.Slider(label="Latent Window Size", minimum=1, maximum=33, value=9, step=1, visible=False) | |
components['output_folder_ui_ctrl'] = gr.Textbox(label="Output Folder", value=workspace_manager.outputs_folder) | |
components['save_as_default_button'] = gr.Button("Save as Default", variant="secondary") | |
components['reset_ui_button'] = gr.Button("Save & Refresh UI", variant="secondary") | |
# UI and Workspace Management Buttons | |
with gr.Row(): | |
components['save_workspace_button'] = gr.Button("Save Workspace", variant="secondary") | |
components['load_workspace_button'] = gr.Button("Load Workspace", variant="secondary") | |
# Column 2: Live Preview and Final Output | |
with gr.Column(scale=1): | |
gr.Markdown("## Live Preview & Output") | |
components['current_task_preview_image_ui'] = gr.Image(interactive=False, visible=False) | |
components['current_task_progress_desc_ui'] = gr.Markdown('') | |
components['current_task_progress_bar_ui'] = gr.HTML('') | |
# Set a height on the video player to help with layout stability | |
components['last_finished_video_ui'] = gr.Video(interactive=True, autoplay=False, height=540) | |
return components |