Spaces:
Sleeping
Sleeping
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() |