import gradio as gr from app_logic import ( create_space, view_space_files, update_space_file, load_token_from_image_and_set_env, # New import KEYLOCK_DECODE_AVAILABLE # New import ) # PIL Image for type hint if needed, though gr.Image handles it # from PIL import Image # Gradio interface def main_ui(): with gr.Blocks(theme=gr.themes.Soft(primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.sky), title="Hugging Face Space Builder") as demo: gr.Markdown( """ # 🛠️ Hugging Face Space Builder Create, view, and update Hugging Face Spaces. Provide your Hugging Face API token directly or load it from a steganographic image. """ ) # --- Authentication Section --- with gr.Accordion("🔑 Authentication Methods", open=True): gr.Markdown( """ **Token Precedence:** 1. If a token is successfully loaded from an image (and `HF_TOKEN` is set in the environment), it will be used. 2. Otherwise, the token entered in the 'Enter API Token Directly' textbox will be used. 3. If a system-wide `HF_TOKEN` environment variable was already set when this app started, it might also be used if methods 1 & 2 don't yield a token. """ ) gr.Markdown("---") gr.Markdown("### Method 1: Enter API Token Directly") api_token_ui_input = gr.Textbox( # Renamed to avoid confusion with resolved_api_token label="Hugging Face API Token (hf_xxx)", type="password", placeholder="Enter your HF token OR load from image below", info="Get from hf.co/settings/tokens. Needs 'write' access. This is used if image loading fails or is not used." ) if KEYLOCK_DECODE_AVAILABLE: gr.Markdown("---") gr.Markdown("### Method 2: Load API Token from Steganographic Image") with gr.Row(): keylock_image_input = gr.Image( label="Stego Image (PNG containing HF_TOKEN)", type="pil", image_mode="RGBA", # << TRY ADDING THIS. Use "RGB" if your images are RGB. # If you're unsure, you can omit it first to see if the debug saving helps. # sources=["upload"], ) keylock_password_input = gr.Textbox( label="Image Password", type="password", placeholder="Password for image decryption" ) keylock_decode_button = gr.Button("Load Token from Image", variant="secondary") keylock_status_output = gr.Markdown(label="Image Decoding Status", value="Status will appear here...") keylock_decode_button.click( fn=load_token_from_image_and_set_env, inputs=[keylock_image_input, keylock_password_input], outputs=[keylock_status_output] ) else: gr.Markdown( "_(Image decoding feature (KeyLock-Decode) is disabled as the library could not be imported.)_" ) # --- Main Application Tabs --- with gr.Tabs(): with gr.TabItem("🚀 Create New Space"): # ... (rest of the Create Space UI, inputs will use api_token_ui_input) with gr.Row(): space_name_create_input = gr.Textbox(label="Space Name", placeholder="my-awesome-app (no slashes)", scale=2) owner_create_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank for your HF username", scale=1) sdk_create_input = gr.Dropdown( label="Space SDK", choices=["gradio", "streamlit", "docker", "static"], value="gradio", info="Select the type of Space." ) markdown_input_create = gr.Textbox( label="Markdown File Structure & Content", placeholder="""Define files using '### File: path/to/file.ext' followed by content, optionally in code blocks. Example: ### File: app.py # ```python print("Hello World!") # ``` """, lines=15, interactive=True, info="Define files using '### File: path/to/your/file.ext' followed by content." ) create_btn = gr.Button("Create Space", variant="primary") create_output_md = gr.Markdown(label="Result") with gr.TabItem("📄 View Space Files"): # ... (rest of the View Space Files UI, inputs will use api_token_ui_input) with gr.Row(): space_name_view_input = gr.Textbox(label="Space Name", placeholder="my-existing-app (no slashes)", scale=2) owner_view_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank if it's your space", scale=1) view_btn = gr.Button("List Files", variant="primary") view_output_md = gr.Markdown(label="Files in Space") with gr.TabItem("✏️ Update Space File"): # ... (rest of the Update Space File UI, inputs will use api_token_ui_input) with gr.Row(): space_name_update_input = gr.Textbox(label="Space Name", placeholder="my-target-app (no slashes)", scale=2) owner_update_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank if it's your space", scale=1) file_path_update_input = gr.Textbox( label="File Path in Repository", placeholder="e.g., app.py or src/utils.py", info="The full path to the file within the space." ) file_content_update_input = gr.Textbox( label="New File Content", placeholder="Enter the complete new content for the file.", lines=10, interactive=True ) commit_message_update_input = gr.Textbox( label="Commit Message", placeholder="e.g., Update app.py with new feature", info="Describe the changes." ) update_btn = gr.Button("Update File", variant="primary") update_output_md = gr.Markdown(label="Result") # Event handlers # Pass the UI token input to all backend functions. # The backend's _get_api_token helper will decide the actual token to use. create_btn.click( fn=create_space, inputs=[api_token_ui_input, space_name_create_input, owner_create_input, sdk_create_input, markdown_input_create], outputs=create_output_md, ) view_btn.click( fn=view_space_files, inputs=[api_token_ui_input, space_name_view_input, owner_view_input], outputs=view_output_md, ) update_btn.click( fn=update_space_file, inputs=[ api_token_ui_input, space_name_update_input, owner_update_input, file_path_update_input, file_content_update_input, commit_message_update_input, ], outputs=update_output_md, ) return demo if __name__ == "__main__": demo = main_ui() demo.launch()