# import gradio as gr # # No os needed here now for file paths unless for basename for display # from app_logic import ( # create_space, # update_space_file, # load_token_from_image_and_set_env, # KEYLOCK_DECODE_AVAILABLE, # list_space_files_for_browsing, # New # get_space_file_content, # New # ) # # 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 manage Hugging Face Spaces. # Provide your Hugging Face API token directly or load it from a KeyLock Wallet image. # """ # ) # # --- Authentication Section (Unchanged) --- # with gr.Accordion("🔑 Authentication Methods", open=True): # gr.Markdown( # """ # Token Precedence: # 1. If a token is successfully loaded from a KeyLock Wallet image, it will be used. # 2. Otherwise, the token entered in the 'Enter API Token Directly' textbox will be used. # """ # ) # gr.Markdown("---") # gr.Markdown("### Method 1: Enter API Token Directly") # api_token_ui_input = gr.Textbox(label="Hugging Face API Token (hf_xxx)", type="password", placeholder="Enter token OR load from Wallet image") # if KEYLOCK_DECODE_AVAILABLE: # gr.Markdown("---") # gr.Markdown("### Method 2: Load API Token from KeyLock Wallet Image") # with gr.Row(): # keylock_image_input = gr.Image(label="KeyLock Wallet Image (PNG)", type="pil", image_mode="RGBA") # keylock_password_input = gr.Textbox(label="Image Password", type="password") # keylock_decode_button = gr.Button("Load Token from Wallet Image", variant="secondary") # keylock_status_output = gr.Markdown(label="Wallet Image Decoding Status", value="Status...") # keylock_decode_button.click(load_token_from_image_and_set_env, [keylock_image_input, keylock_password_input], [keylock_status_output]) # else: # gr.Markdown("(KeyLock Wallet image decoding disabled: library not found.)") # # --- Main Application Tabs --- # with gr.Tabs(): # with gr.TabItem("🚀 Create New Space"): # # (Create Space UI Unchanged) # 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") # markdown_input_create = gr.Textbox(label="Markdown File Structure & Content", placeholder="Example:\n### File: app.py\n# python\nprint(\"Hello\")\n#", lines=15, interactive=True) # create_btn = gr.Button("Create Space", variant="primary") # create_output_md = gr.Markdown(label="Result") # create_btn.click(create_space, [api_token_ui_input, space_name_create_input, owner_create_input, sdk_create_input, markdown_input_create], create_output_md) # # --- "Browse & Edit Files" Tab (Hub-based) --- # with gr.TabItem("📂 Browse & Edit Space Files"): # gr.Markdown("Browse, view, and edit files directly on a Hugging Face Space.") # with gr.Row(): # browse_space_name_input = gr.Textbox(label="Space Name", placeholder="my-target-app", scale=2) # browse_owner_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank if it's your space", scale=1) # browse_load_files_button = gr.Button("Load Files List from Space", variant="secondary") # browse_status_output = gr.Markdown(label="File List Status") # gr.Markdown("---") # gr.Markdown("### Select File to View/Edit") # # Using Radio for file list. Could be Dropdown for many files. # # choices will be updated dynamically. # file_selector_radio = gr.Radio( # label="Files in Space", # choices=[], # interactive=True, # info="Select a file to load its content below." # ) # gr.Markdown("---") # gr.Markdown("### File Editor") # file_editor_textbox = gr.Textbox( # label="File Content (Editable)", lines=20, interactive=True, # placeholder="Select a file from the list above to view/edit its content." # ) # edit_commit_message_input = gr.Textbox(label="Commit Message for Update", placeholder="e.g., Update app.py content") # update_edited_file_button = gr.Button("Update File in Space", variant="primary") # edit_update_status_output = gr.Markdown(label="File Update Result") # # --- Event Handlers for Browse & Edit Tab (Hub-based) --- # def handle_load_space_files_list(token_from_ui, space_name, owner_name): # if not space_name: # return { # browse_status_output: gr.Markdown("Error: Space Name cannot be empty."), # file_selector_radio: gr.Radio(choices=[], value=None), # Clear radio # file_editor_textbox: gr.Textbox(value=""), # Clear editor # } # files_list, error_msg = list_space_files_for_browsing(token_from_ui, space_name, owner_name) # if error_msg and files_list is None: # Indicates a hard error # return { # browse_status_output: gr.Markdown(f"Error: {error_msg}"), # file_selector_radio: gr.Radio(choices=[], value=None), # file_editor_textbox: gr.Textbox(value=""), # } # if error_msg and not files_list: # Info message like "no files found" # return { # browse_status_output: gr.Markdown(error_msg), # Show "No files found" # file_selector_radio: gr.Radio(choices=[], value=None), # file_editor_textbox: gr.Textbox(value=""), # } # return { # browse_status_output: gr.Markdown(f"Files loaded for '{owner_name}/{space_name}'. Select a file to edit."), # file_selector_radio: gr.Radio(choices=files_list, value=None, label=f"Files in {owner_name}/{space_name}"), # file_editor_textbox: gr.Textbox(value=""), # Clear editor on new list load # } # browse_load_files_button.click( # fn=handle_load_space_files_list, # inputs=[api_token_ui_input, browse_space_name_input, browse_owner_input], # outputs=[browse_status_output, file_selector_radio, file_editor_textbox] # ) # def handle_file_selected_for_editing(token_from_ui, space_name, owner_name, selected_filepath_evt: gr.SelectData): # if not selected_filepath_evt or not selected_filepath_evt.value: # # This might happen if the radio is cleared or has no selection # return { # file_editor_textbox: gr.Textbox(value=""), # browse_status_output: gr.Markdown("No file selected or selection cleared.") # } # selected_filepath = selected_filepath_evt.value # The value of the selected radio button # if not space_name: # Should not happen if file list is populated # return { # file_editor_textbox: gr.Textbox(value="Error: Space name is missing."), # browse_status_output: gr.Markdown("Error: Space context lost. Please reload file list.") # } # content, error_msg = get_space_file_content(token_from_ui, space_name, owner_name, selected_filepath) # if error_msg: # return { # file_editor_textbox: gr.Textbox(value=f"Error loading file content: {error_msg}"), # browse_status_output: gr.Markdown(f"Failed to load '{selected_filepath}': {error_msg}") # } # return { # file_editor_textbox: gr.Textbox(value=content), # browse_status_output: gr.Markdown(f"Content loaded for: {selected_filepath}") # } # # Use .select event for gr.Radio # file_selector_radio.select( # fn=handle_file_selected_for_editing, # inputs=[api_token_ui_input, browse_space_name_input, browse_owner_input], # Pass space context again # outputs=[file_editor_textbox, browse_status_output] # ) # update_edited_file_button.click( # fn=update_space_file, # inputs=[ # api_token_ui_input, # browse_space_name_input, # browse_owner_input, # file_selector_radio, # Pass the selected file path from the radio # file_editor_textbox, # edit_commit_message_input # ], # outputs=[edit_update_status_output] # ) # return demo # if name == "main": # demo = main_ui() # demo.launch()