File size: 7,703 Bytes
60d3ef3
 
 
 
 
f558e4b
 
8bd1285
f558e4b
 
8bd1285
60d3ef3
 
 
 
 
 
f558e4b
 
60d3ef3
 
 
f558e4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba9e0a6
 
 
 
f558e4b
 
 
 
 
 
 
 
 
 
 
 
 
6063cc9
f558e4b
 
 
 
 
 
60d3ef3
 
f558e4b
60d3ef3
 
 
 
f558e4b
 
60d3ef3
 
 
 
 
 
 
 
 
 
f558e4b
 
 
60d3ef3
 
 
 
 
f558e4b
60d3ef3
 
 
 
 
 
 
f558e4b
60d3ef3
 
 
 
f558e4b
 
 
60d3ef3
f558e4b
 
60d3ef3
 
f558e4b
 
60d3ef3
 
 
 
 
f558e4b
 
60d3ef3
 
f558e4b
60d3ef3
 
 
 
f558e4b
60d3ef3
 
 
 
 
f558e4b
60d3ef3
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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()