broadfield-dev commited on
Commit
9a9ea97
·
verified ·
1 Parent(s): e89f2fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -246
app.py CHANGED
@@ -1,251 +1,63 @@
1
  import gradio as gr
2
- from app_logic import (
3
- create_space,
4
- update_space_file,
5
- load_token_from_image_and_set_env,
6
- KEYLOCK_DECODE_AVAILABLE,
7
- list_space_files_for_browsing,
8
- get_space_file_content,
9
- )
10
- KEYLOCK_DECODE_AVAILABLE = False
11
- # Gradio interface
12
- def main_ui():
13
- 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:
14
- gr.Markdown(
15
- """
16
- # 🛠️ Hugging Face Space Builder
17
- ## Build Huggingface Space from a standardized string that models can be prompted to export.
18
-
19
- Create, view, and manage Hugging Face Spaces.
20
- Provide your Hugging Face API token directly or load it from a KeyLock Wallet image.
21
- """
22
- )
23
-
24
- # --- Authentication Section (Unchanged) ---
25
- with gr.Accordion("🔑 Authentication Methods", open=False):
26
- gr.Markdown(
27
- """
28
- **Token Precedence:**
29
- 1. If a token is successfully loaded from a KeyLock Wallet image, it will be used.
30
- 2. Otherwise, the token entered in the 'Enter API Token Directly' textbox will be used.
31
- """
32
  )
33
- gr.Markdown("---")
34
- gr.Markdown("### Method 1: Enter API Token Directly")
35
- api_token_ui_input = gr.Textbox(label="Hugging Face API Token (hf_xxx)", type="password", placeholder="Enter token OR load from Wallet image")
36
- if KEYLOCK_DECODE_AVAILABLE:
37
- gr.Markdown("---")
38
- gr.Markdown("### Method 2: Load API Token to this sytem environment from KeyLock Wallet Image")
39
- gr.Markdown("### Get a KeyLock Wallet Image Here: [/spaces/broadfield-dev/KeyLock-API-Wallet](https://huggingface.co/spaces/broadfield-dev/KeyLock-API-Wallet)")
40
- with gr.Row():
41
- keylock_image_input = gr.Image(label="KeyLock Wallet Image (PNG)", type="pil", image_mode="RGBA")
42
- keylock_password_input = gr.Textbox(label="Image Password", type="password")
43
- keylock_decode_button = gr.Button("Load Token from Wallet Image", variant="secondary")
44
- keylock_status_output = gr.Markdown(label="Wallet Image Decoding Status", value="Status...")
45
- keylock_decode_button.click(load_token_from_image_and_set_env, [keylock_image_input, keylock_password_input], [keylock_status_output])
46
- else:
47
- gr.Markdown("_(KeyLock Wallet image decoding disabled: library not found.)_")
48
- with gr.Accordion("📣 Example Prompt", open=False):
49
- gr.Markdown("""```plaintext
50
- # Prompt:
51
-
52
- Generate program files for a project as a single plain text string, strictly adhering to the markdown format below. **Every single line**, including backticks, language identifiers, file content, and empty lines, **must** be prefixed with '# ' to comment it out. This is critical to avoid code box interference. The output must include a complete file structure and the contents of each file, with all necessary code and configurations for a functional project. **Do not deviate from this format under any circumstances.**
53
-
54
- # Format (exact return format with single leading "# "):
55
-
56
- # # Space: [owner/project-name]
57
- # ## File Structure
58
- # ```
59
- # 📁 Root
60
- # 📄 [file1]
61
- # 📄 [file2]
62
- # ...
63
- #
64
- #
65
- # Below are the contents of all files in the space:
66
- #
67
- # ### File: [file1]
68
- # ```[language]
69
- # [content]
70
- # ```
71
- #
72
- # ### File: [file2]
73
- # ```[language]
74
- # [content]
75
- # ```
76
- #
77
- # ... (repeat for each file)
78
- # Correct Example Output (exact, every line prefixed with '# '):
79
- # # Space: user/my-app
80
- # ## File Structure
81
- # ```
82
- # 📁 Root
83
- # 📄 app.py
84
- # 📄 requirements.txt
85
- # ```
86
- #
87
- # # Below are the contents of all files in the space:
88
- #
89
- # ### File: app.py
90
- # ```python
91
- # print("Hello, World!")
92
- # ```
93
- #
94
- # ### File: requirements.txt
95
- # ```text
96
- # gradio==4.44.0
97
- # ```
98
- # Incorrect Example Output:
99
- ## ## File Structure <- INCORRECT: AI used "## " instead of "# "
100
- ## ```text <- INCORRECT
101
- ## 📁 Root <- INCORRECT (missing "# ")
102
- # # # 📁 Root <- INCORRECT: AI used "# # #" instead of "# "
103
-
104
- # Instructions:
105
- - Use exactly `# # Space: [owner/project-name]` as the header (e.g., `user/my-app`).
106
- - Under `## File Structure`, start with `# 📁 Root` followed by `# 📄` for each file, using exact icons and spacing.
107
- - For each file, use `# ### File: [filename]` followed by a code block where every line, including backticks (e.g., `# ```), language identifier (e.g., `# python`), and content (e.g., `# print("Hello")`), is prefixed with `# `.
108
- - For binary files, use `# [Binary file - size in bytes]` as content with no language identifier.
109
- - **Every line** must start with `# `, no exceptions, including empty lines within code blocks.
110
- - Provide accurate, functional code or content for each file, suitable for the project’s purpose.
111
- - Ensure the output is concise, complete, and parseable to extract file structure and contents.
112
- - Output everything as a single plain text string within one code box.
113
- ```
114
- """
115
- )
116
-
117
- # --- Main Application Tabs ---
118
- with gr.Tabs():
119
- with gr.TabItem("🚀 Create New Space"):
120
- # (Create Space UI Unchanged)
121
- with gr.Row():
122
- space_name_create_input = gr.Textbox(label="Space Name", placeholder="my-awesome-app (no slashes)", scale=2)
123
- owner_create_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank for your HF username", scale=1)
124
- sdk_create_input = gr.Dropdown(label="Space SDK", choices=["gradio", "streamlit", "docker", "static"], value="gradio")
125
- gr.Markdown("### Example Source: [/spaces/broadfield-dev/repo_to_md](https://huggingface.co/spaces/broadfield-dev/repo_to_md)")
126
- 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)
127
- create_btn = gr.Button("Create Space", variant="primary")
128
- create_output_md = gr.Markdown(label="Result")
129
- 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)
130
-
131
-
132
- # --- "Browse & Edit Files" Tab (Hub-based) ---
133
- with gr.TabItem("📂 Browse & Edit Space Files"):
134
- gr.Markdown("Browse, view, and edit files directly on a Hugging Face Space.")
135
- with gr.Row():
136
- browse_space_name_input = gr.Textbox(label="Space Name", placeholder="my-target-app", scale=2)
137
- browse_owner_input = gr.Textbox(label="Owner Username/Org", placeholder="Leave blank if it's your space", scale=1)
138
-
139
- browse_load_files_button = gr.Button("Load Files List from Space", variant="secondary")
140
- browse_status_output = gr.Markdown(label="File List Status")
141
 
142
- gr.Markdown("---")
143
- gr.Markdown("### Select File to View/Edit")
144
- # Using Radio for file list. Could be Dropdown for many files.
145
- # `choices` will be updated dynamically.
146
- file_selector_radio = gr.Radio(
147
- label="Files in Space",
148
- choices=[],
149
- interactive=True,
150
- info="Select a file to load its content below."
151
- )
152
-
153
- gr.Markdown("---")
154
- gr.Markdown("### File Editor")
155
- file_editor_textbox = gr.Textbox(
156
- label="File Content (Editable)", lines=20, interactive=True,
157
- placeholder="Select a file from the list above to view/edit its content."
158
- )
159
- edit_commit_message_input = gr.Textbox(label="Commit Message for Update", placeholder="e.g., Update app.py content")
160
- update_edited_file_button = gr.Button("Update File in Space", variant="primary")
161
- edit_update_status_output = gr.Markdown(label="File Update Result")
162
-
163
- # --- Event Handlers for Browse & Edit Tab (Hub-based) ---
164
- def handle_load_space_files_list(token_from_ui, space_name, owner_name):
165
- if not space_name:
166
- return {
167
- browse_status_output: gr.Markdown("Error: Space Name cannot be empty."),
168
- file_selector_radio: gr.Radio(choices=[], value=None), # Clear radio
169
- file_editor_textbox: gr.Textbox(value=""), # Clear editor
170
- }
171
-
172
- files_list, error_msg = list_space_files_for_browsing(token_from_ui, space_name, owner_name)
173
-
174
- if error_msg and files_list is None: # Indicates a hard error
175
- return {
176
- browse_status_output: gr.Markdown(f"Error: {error_msg}"),
177
- file_selector_radio: gr.Radio(choices=[], value=None),
178
- file_editor_textbox: gr.Textbox(value=""),
179
- }
180
- if error_msg and not files_list: # Info message like "no files found"
181
- return {
182
- browse_status_output: gr.Markdown(error_msg), # Show "No files found"
183
- file_selector_radio: gr.Radio(choices=[], value=None),
184
- file_editor_textbox: gr.Textbox(value=""),
185
- }
186
-
187
- return {
188
- browse_status_output: gr.Markdown(f"Files loaded for '{owner_name}/{space_name}'. Select a file to edit."),
189
- file_selector_radio: gr.Radio(choices=files_list, value=None, label=f"Files in {owner_name}/{space_name}"),
190
- file_editor_textbox: gr.Textbox(value=""), # Clear editor on new list load
191
- }
192
-
193
- browse_load_files_button.click(
194
- fn=handle_load_space_files_list,
195
- inputs=[api_token_ui_input, browse_space_name_input, browse_owner_input],
196
- outputs=[browse_status_output, file_selector_radio, file_editor_textbox]
197
- )
198
-
199
- def handle_file_selected_for_editing(token_from_ui, space_name, owner_name, selected_filepath_evt: gr.SelectData):
200
- if not selected_filepath_evt or not selected_filepath_evt.value:
201
- # This might happen if the radio is cleared or has no selection
202
- return {
203
- file_editor_textbox: gr.Textbox(value=""),
204
- browse_status_output: gr.Markdown("No file selected or selection cleared.")
205
- }
206
-
207
- selected_filepath = selected_filepath_evt.value # The value of the selected radio button
208
-
209
- if not space_name: # Should not happen if file list is populated
210
- return {
211
- file_editor_textbox: gr.Textbox(value="Error: Space name is missing."),
212
- browse_status_output: gr.Markdown("Error: Space context lost. Please reload file list.")
213
- }
214
-
215
- content, error_msg = get_space_file_content(token_from_ui, space_name, owner_name, selected_filepath)
216
-
217
- if error_msg:
218
- return {
219
- file_editor_textbox: gr.Textbox(value=f"Error loading file content: {error_msg}"),
220
- browse_status_output: gr.Markdown(f"Failed to load '{selected_filepath}': {error_msg}")
221
- }
222
-
223
- return {
224
- file_editor_textbox: gr.Textbox(value=content),
225
- browse_status_output: gr.Markdown(f"Content loaded for: {selected_filepath}")
226
- }
227
-
228
- # Use .select event for gr.Radio
229
- file_selector_radio.select(
230
- fn=handle_file_selected_for_editing,
231
- inputs=[api_token_ui_input, browse_space_name_input, browse_owner_input], # Pass space context again
232
- outputs=[file_editor_textbox, browse_status_output]
233
- )
234
-
235
- update_edited_file_button.click(
236
- fn=update_space_file,
237
- inputs=[
238
- api_token_ui_input,
239
- browse_space_name_input,
240
- browse_owner_input,
241
- file_selector_radio, # Pass the selected file path from the radio
242
- file_editor_textbox,
243
- edit_commit_message_input
244
- ],
245
- outputs=[edit_update_status_output]
246
- )
247
- return demo
248
 
249
  if __name__ == "__main__":
250
- demo = main_ui()
251
- demo.launch(mcp_server=True,show_error=True)
 
1
  import gradio as gr
2
+ from keylock_component import KeylockDecoderComponent, AppServerLogic
3
+
4
+ shared_server_logic = AppServerLogic()
5
+
6
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="sky")) as demo:
7
+ gr.Markdown("# 🔑 KeyLock Application Login")
8
+
9
+ with gr.Row():
10
+ with gr.Column(scale=2):
11
+ gr.Markdown("### 1. Login with Credentials")
12
+ gr.Markdown("Enter your credentials manually, or use a KeyLock image to auto-fill and log in.")
13
+ username_input = gr.Textbox(label="Username", interactive=True)
14
+ password_input = gr.Textbox(label="Password", type="password", interactive=True)
15
+ login_button = gr.Button("Login", variant="primary")
16
+ login_status = gr.Markdown()
17
+
18
+ with gr.Column(scale=3):
19
+ gr.Markdown("### 2. Use a KeyLock Image to Auto-Login")
20
+ keylock_decoder = KeylockDecoderComponent(
21
+ server_logic=shared_server_logic
 
 
 
 
 
 
 
 
 
 
22
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ def handle_login(username, password):
25
+ user_data = shared_server_logic.mock_user_db.get(username)
26
+ if user_data and user_data["password"] == password:
27
+ gr.Info(f"Login Successful! Welcome, {username}.")
28
+ return f"<p style='color:green;font-weight:bold'>✅ Login successful for {username}.</p>"
29
+ else:
30
+ gr.Error("Login Failed: Invalid username or password.")
31
+ return f"<p style='color:red;font-weight:bold'>❌ Login failed.</p>"
32
+
33
+ def autofill_and_login(decoder_result):
34
+ if decoder_result and decoder_result.get("status") == "Success":
35
+ payload = decoder_result.get("payload", {})
36
+ user = payload.get("USER", "")
37
+ user_data = shared_server_logic.mock_user_db.get(user)
38
+ password = user_data.get("password", "") if user_data else ""
39
+
40
+ login_message = handle_login(user, password)
41
+
42
+ return gr.update(value=user), gr.update(value=password), login_message
43
+
44
+ elif decoder_result:
45
+ gr.Warning("KeyLock decoding failed. Please try again or enter credentials manually.")
46
+ return gr.update(value=""), gr.update(value=""), "<p style='color:orange;font-weight:bold'>❌ KeyLock read failed.</p>"
47
+
48
+ return gr.update(), gr.update(), gr.update()
49
+
50
+ keylock_decoder.change(
51
+ fn=autofill_and_login,
52
+ inputs=[keylock_decoder],
53
+ outputs=[username_input, password_input, login_status]
54
+ )
55
+
56
+ login_button.click(
57
+ fn=handle_login,
58
+ inputs=[username_input, password_input],
59
+ outputs=[login_status]
60
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  if __name__ == "__main__":
63
+ demo.launch()