broadfield-dev commited on
Commit
bb7bc48
·
verified ·
1 Parent(s): c07bdc1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -81
app.py CHANGED
@@ -16,14 +16,15 @@ from build_logic import (
16
  parse_markdown as build_logic_parse_markdown,
17
  delete_space_file as build_logic_delete_space_file,
18
  get_space_runtime_status,
19
- apply_staged_file_changes, # Renamed to indicate it only handles files
20
  duplicate_space as build_logic_duplicate_space,
21
  list_user_spaces as build_logic_list_user_spaces,
22
  build_logic_set_space_privacy,
23
  build_logic_delete_space,
24
  build_logic_create_pull_request,
25
  build_logic_add_comment,
26
- build_logic_like_space
 
27
  )
28
 
29
  from model_logic import (
@@ -110,7 +111,7 @@ def _infer_lang_from_filename(filename):
110
  'rb': 'ruby', 'php': 'php', 'go': 'go', 'rs': 'rust', 'swift': 'swift', 'kt': 'kotlin', 'kts': 'kotlin',
111
  'sql': 'sql', 'dockerfile': 'docker', 'tf': 'terraform', 'hcl': 'terraform',
112
  'txt': 'plaintext', 'log': 'plaintext', 'ini': 'ini', 'conf': 'plaintext', 'cfg': 'plaintext',
113
- 'csv': 'plaintext', 'tsv': 'plaintext', 'err': 'plaintext',
114
  '.env': 'plaintext', '.gitignore': 'plaintext', '.npmrc': 'plaintext', '.gitattributes': 'plaintext',
115
  'makefile': 'makefile',
116
  }
@@ -354,13 +355,12 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
354
  print(f"Staged DELETE_SPACE action for {hf_owner_name}/{hf_repo_name}")
355
 
356
  elif command == "CREATE_PR" and args:
357
- # Expecting: CREATE_PR <target_repo_id> --title "..." --body "..."
358
  target_repo_id = None
359
  title = ""
360
  body = ""
361
  try:
362
  target_repo_id = args[0]
363
- arg_string = shlex.join(args[1:]) # Rejoin remaining args as a string to parse options
364
 
365
  title_match = re.search(r'--title\s+"([^"]*)"', arg_string)
366
  if title_match: title = title_match.group(1)
@@ -370,7 +370,7 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
370
  if body_match: body = body_match.group(1)
371
  else: print("Warning: CREATE_PR missing --body argument.")
372
 
373
- if target_repo_id and title: # Require target and title
374
  changeset.append({"type": "CREATE_PR", "source_repo_id": f"{hf_owner_name}/{hf_repo_name}", "target_repo_id": target_repo_id, "title": title, "body": body})
375
  print(f"Staged CREATE_PR action to {target_repo_id}")
376
  else:
@@ -382,16 +382,12 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
382
 
383
 
384
  elif command == "ADD_COMMENT" and args:
385
- # Expecting: ADD_COMMENT "Your comment text"
386
  comment_text = None
387
  try:
388
- # Attempt to parse the rest of the line as a single quoted string
389
- # Rejoin args and then try to find the quoted string
390
  comment_string = shlex.join(args)
391
- comment_match = re.match(r'^"([^"]*)"$', comment_string) # Look for the whole string being a quoted value
392
  if comment_match: comment_text = comment_match.group(1)
393
  else:
394
- # Fallback: just use the raw args joined by space if not a perfect quoted string
395
  comment_text = " ".join(args)
396
  print(f"Warning: ADD_COMMENT argument was not a single quoted string, using raw args: '{comment_text}'")
397
 
@@ -408,7 +404,6 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
408
 
409
 
410
  elif command == "LIKE_SPACE":
411
- # No args expected
412
  if hf_owner_name and hf_repo_name:
413
  changeset.append({"type": "LIKE_SPACE", "repo_id": f"{hf_owner_name}/{hf_repo_name}"})
414
  print(f"Staged LIKE_SPACE action on {hf_owner_name}/{hf_repo_name}")
@@ -416,9 +411,6 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
416
  print(f"Warning: Cannot stage LIKE_SPACE action, no Space currently loaded.")
417
  changeset.append({"type": "Error", "message": "Cannot like space, no Space currently loaded."})
418
 
419
- # Note: DUPLICATE_SPACE is handled before this loop
420
-
421
-
422
  for file_info in ai_proposed_files_list:
423
  filename = file_info["path"]
424
  proposed_content = file_info["content"]
@@ -456,6 +448,8 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
456
  for change in space_actions:
457
  if change["type"] == "CREATE_SPACE":
458
  md_summary.append(f"- **🚀 Create New Space:** `{change.get('repo_id', '...')}` (SDK: {change.get('sdk', 'gradio')}, Private: {change.get('private', False)})")
 
 
459
  elif change["type"] == "SET_PRIVACY":
460
  md_summary.append(f"- **🔒 Set Privacy:** Set `{change.get('repo_id', '...')}` to `private={change.get('private', False)}`")
461
  elif change["type"] == "DELETE_SPACE":
@@ -466,7 +460,7 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
466
  if change.get('body'): md_summary.append(f" - Body: `{change.get('body', '...')}`")
467
  elif change["type"] == "ADD_COMMENT":
468
  md_summary.append(f"- **💬 Add Comment:** On `{change.get('repo_id', '...')}`")
469
- md_summary.append(f" - Comment: `{change.get('comment', '...')[:100]}{'...' if len(change.get('comment', '')) > 100 else ''}`") # Truncate long comments
470
  elif change["type"] == "LIKE_SPACE":
471
  md_summary.append(f"- **👍 Like Space:** `{change.get('repo_id', '...')}`")
472
  elif change["type"] == "Error":
@@ -598,15 +592,13 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
598
  global parsed_code_blocks_state_cache
599
 
600
  _status = "Applying changes..."
601
- # Yield initial status, hide confirm UI, clear summary
602
  yield _status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="*Applying changes...*")
603
- # Include outputs for potential space load/update
604
- yield gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # Dummy yields to match structure
605
 
606
  if not changeset:
607
  return "No changes to apply.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="No changes were staged.")
608
 
609
- # Check if the first action is an exclusive action like DUPLICATE_SPACE
610
  first_action = changeset[0] if changeset else None
611
  is_exclusive_duplicate = first_action and first_action.get('type') == 'DUPLICATE_SPACE'
612
  is_exclusive_delete = first_action and first_action.get('type') == 'DELETE_SPACE'
@@ -621,20 +613,17 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
621
  target_repo_id=change["target_repo_id"],
622
  private=change.get("private", False)
623
  )
624
- # After duplication, attempt to load the *new* space
625
  _status_reload = f"{status_message} | Attempting to load the new Space [{change['target_repo_id']}]..."
626
- yield gr.update(value=_status_reload) # Update status
627
 
628
- # Extract new owner and space name from target_repo_id
629
  target_repo_id = change['target_repo_id']
630
  new_owner, new_space_name = target_repo_id.split('/', 1) if '/' in target_repo_id else (None, target_repo_id)
631
 
632
- # If owner was not explicitly provided in target_repo_id, try to get it from the token
633
  if not new_owner:
634
  token, token_err = build_logic_get_api_token(hf_api_key)
635
  if token_err:
636
  print(f"Error getting token for new owner determination: {token_err}")
637
- new_owner = None # Cannot auto-detect
638
  else:
639
  try: user_info = build_logic_whoami(token=token); new_owner = user_info.get('name')
640
  except Exception as e:
@@ -642,7 +631,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
642
  new_owner = None
643
 
644
 
645
- # Trigger the load logic for the new space if owner and name are available
646
  if new_owner and new_space_name:
647
  sdk, file_list, err_list = get_space_repository_info(hf_api_key, new_space_name, new_owner)
648
  if err_list:
@@ -673,36 +661,30 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
673
  else:
674
  iframe_update = gr.update(value=None, visible=False)
675
 
676
- # Fetch runtime status for the new space
677
  runtime_status_md = handle_refresh_space_status(hf_api_key, new_owner, new_space_name)
678
 
679
- # Final yield after loading the new space
680
  yield (
681
- gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download, # Status, markdown, preview, download
682
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), # Hide confirm UI
683
- [], gr.update(value="*No changes proposed.*"), # Clear changeset state and display
684
- owner_update, space_update, file_browser_update, iframe_update, # Update space/owner fields, file browser, iframe
685
- gr.update(value=runtime_status_md) # Update runtime status
686
  )
687
 
688
  else:
689
  reload_error = "Cannot load new Space state: Owner or Space Name missing after duplication."
690
- _formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None) # Clear previews if new space couldn't be loaded
691
  final_overall_status = status_message + f" | Reload Status: {reload_error}"
692
- # Keep old UI fields, clear file browser/iframe
693
  yield (
694
  gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
695
  gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
696
  [], gr.update(value="*No changes proposed.*"),
697
  gr.update(), gr.update(), gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False),
698
- gr.update(value="*Runtime status unavailable for new space.*") # Clear runtime status
699
  )
700
 
701
-
702
  else:
703
- # Should not happen if staged_changeset exists and type is DUPLICATE_SPACE, but as safeguard
704
  final_overall_status = "Duplicate Action Error: Invalid duplicate changeset format."
705
- # No state change, just report error
706
  _formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
707
  yield (
708
  gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
@@ -713,7 +695,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
713
 
714
  elif is_exclusive_delete:
715
  change = first_action
716
- # Confirm DELETE_SPACE is only for the currently loaded space before calling the backend
717
  delete_owner = change.get('owner') or owner_name
718
  delete_space = change.get('space_name') or space_name
719
  delete_repo_id_target = f"{delete_owner}/{delete_space}" if delete_owner and delete_space else None
@@ -725,24 +706,21 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
725
  else:
726
  status_message = build_logic_delete_space(hf_api_key, delete_owner, delete_space)
727
  final_overall_status = status_message
728
- # Clear the UI completely after successful deletion
729
  if "Successfully" in status_message:
 
730
  parsed_code_blocks_state_cache = []
731
- _formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None) # Clear previews
732
  owner_update = gr.update(value="")
733
  space_update = gr.update(value="")
734
  file_browser_update = gr.update(visible=False, choices=[], value=None)
735
  iframe_update = gr.update(value=None, visible=False)
736
  runtime_status_update = gr.update(value="*Space deleted, runtime status unavailable.*")
737
  else:
738
- # If deletion failed, keep the current state and update status
739
  _formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
740
  owner_update = gr.update()
741
  space_update = gr.update()
742
  file_browser_update = gr.update()
743
  iframe_update = gr.update()
744
- runtime_status_update = gr.update() # Keep existing or trigger refresh?
745
- # Let's trigger a status refresh after failed delete attempt
746
  runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
747
 
748
 
@@ -755,36 +733,35 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
755
  )
756
 
757
 
758
- else: # Not an exclusive action, process other actions and file changes sequentially
759
-
760
  action_status_messages = []
761
  file_change_operations = []
762
 
763
- # Process non-file actions first
764
  for change in changeset:
765
  try:
766
  if change['type'] == 'CREATE_SPACE':
767
  repo_id_to_create = change.get('repo_id')
768
  if repo_id_to_create:
769
- msg = build_logic_create_space(hf_api_key, repo_id_to_create, change.get('sdk', 'gradio'), change.get('private', False))
770
  action_status_messages.append(f"CREATE_SPACE: {msg}")
 
 
771
  else:
772
  action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
773
  elif change['type'] == 'SET_PRIVACY':
774
- repo_id_to_set_privacy = change.get('repo_id') or f"{owner_name}/{space_name}"
775
- if repo_id_to_set_privacy and owner_name and space_name: # Ensure current space is loaded
776
  msg = build_logic_set_space_privacy(hf_api_key, repo_id_to_set_privacy, change['private'])
777
  action_status_messages.append(f"SET_PRIVACY: {msg}")
778
- else:
779
  action_status_messages.append("SET_PRIVACY Error: Cannot set privacy, no Space currently loaded.")
780
  elif change['type'] == 'CREATE_PR':
781
- # Ensure source is the currently loaded space
782
  source_repo = f"{owner_name}/{space_name}" if owner_name and space_name else None
783
  if source_repo and change.get('target_repo_id') and change.get('title'):
784
  msg = build_logic_create_pull_request(hf_api_key, source_repo, change['target_repo_id'], change['title'], change.get('body', ''))
785
  action_status_messages.append(f"CREATE_PR: {msg}")
786
  else:
787
- action_status_messages.append("CREATE_PR Error: Source/Target repo, title, or body missing.")
788
  elif change['type'] == 'ADD_COMMENT':
789
  repo_id_to_comment = change.get('repo_id') or f"{owner_name}/{space_name}"
790
  if repo_id_to_comment and owner_name and space_name and change.get('comment'):
@@ -802,7 +779,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
802
  elif change['type'] == 'Error':
803
  action_status_messages.append(f"Staging Error: {change.get('message', 'Unknown error')}")
804
 
805
- # Collect file changes for the final commit
806
  elif change['type'] in ['CREATE_FILE', 'UPDATE_FILE', 'DELETE_FILE']:
807
  file_change_operations.append(change)
808
 
@@ -812,8 +788,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
812
  import traceback
813
  traceback.print_exc()
814
 
815
-
816
- # Apply file changes if any were staged
817
  file_commit_status = ""
818
  if file_change_operations:
819
  if owner_name and space_name:
@@ -821,18 +795,18 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
821
  else:
822
  file_commit_status = "File Commit Error: Cannot commit file changes, no Space currently loaded."
823
  action_status_messages.append(file_commit_status)
 
 
824
 
825
 
826
- # Combine all status messages
827
  final_overall_status = " | ".join(action_status_messages)
828
  if file_commit_status and file_commit_status != "No file changes (create/update/delete) to commit.":
829
  if final_overall_status: final_overall_status += " | "
830
  final_overall_status += file_commit_status
831
  if not final_overall_status: final_overall_status = "No operations were applied."
832
 
833
- # After applying changes, reload the space state to reflect the actual state on the Hub
834
  _status_reload = f"{final_overall_status} | Reloading Space state..."
835
- yield gr.update(value=_status_reload) # Update status
836
 
837
  refreshed_file_list = []
838
  reload_error = None
@@ -863,7 +837,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
863
  else:
864
  iframe_update = gr.update(value=None, visible=False)
865
 
866
- # Refresh runtime status as well
867
  runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
868
 
869
 
@@ -908,6 +881,42 @@ def update_models_dropdown(provider_select):
908
  selected_value = default_model if default_model in models else (models[0] if models else None)
909
  return gr.update(choices=models, value=selected_value)
910
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
911
  def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
912
  global parsed_code_blocks_state_cache
913
  _formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
@@ -916,7 +925,8 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
916
  _changeset_clear = []
917
  _changeset_summary_clear = "*No changes proposed.*"
918
  _confirm_ui_hidden = gr.update(visible=False)
919
- _list_spaces_display_clear = "*List of spaces will appear here.*"
 
920
 
921
 
922
  yield (
@@ -925,7 +935,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
925
  _iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
926
  gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
927
  _changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
928
- gr.update(value=_list_spaces_display_clear)
929
  )
930
 
931
  owner_to_use = ui_owner_name
@@ -963,7 +973,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
963
  gr.update(visible=False, choices=[], value=None),
964
  gr.update(), gr.update(),
965
  gr.update(value=None, visible=False),
966
- _download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
967
  )
968
  return
969
 
@@ -997,7 +1007,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
997
  file_browser_update,
998
  gr.update(), gr.update(),
999
  iframe_update,
1000
- _download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1001
  )
1002
 
1003
  def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, is_private_ui, formatted_markdown_content):
@@ -1032,9 +1042,6 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
1032
  yield gr.update(value=_build_status),
1033
  return
1034
 
1035
- # Use apply_staged_file_changes and CREATE_SPACE action via confirm handler logic
1036
- # Or simulate the parts needed here directly? Direct call might be simpler for manual build.
1037
- # Let's use apply_staged_file_changes directly, but need to handle the CREATE_SPACE part separately first.
1038
  token, token_err = build_logic_get_api_token(hf_api_key_ui)
1039
  if token_err:
1040
  _build_status = f"Build Error: API Token Error: {token_err}"
@@ -1045,19 +1052,17 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
1045
 
1046
  build_status_messages = []
1047
  try:
1048
- # Attempt to create/ensure space exists first
1049
  api.create_repo(repo_id=repo_id_target, repo_type="space", space_sdk=space_sdk_select, private=is_private_ui, exist_ok=True)
1050
  build_status_messages.append(f"CREATE_SPACE: Successfully created or ensured space [{repo_id_target}](https://huggingface.co/spaces/{repo_id_target}) exists.")
1051
  except HfHubHTTPError as e_http:
1052
  build_status_messages.append(f"CREATE_SPACE HTTP Error ({e_http.response.status_code if e_http.response else 'N/A'}): {e_http.response.text if e_http.response else str(e_http)}. Check logs.")
1053
  if e_http.response and e_http.response.status_code in (401, 403):
1054
- _build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status); return
1055
  except Exception as e:
1056
  build_status_messages.append(f"CREATE_SPACE Error: {e}")
1057
- _build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status); return
1058
 
1059
 
1060
- # Now apply file changes from the markdown content
1061
  file_changes_only_changeset = [{"type": "CREATE_FILE", "path": f["path"], "content": f["content"], "lang": _infer_lang_from_filename(f["path"])} for f in proposed_files_list]
1062
  if file_changes_only_changeset:
1063
  file_commit_status = apply_staged_file_changes(hf_api_key_ui, ui_owner_name_part, ui_space_name_part, file_changes_only_changeset)
@@ -1079,7 +1084,7 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
1079
  gr.update(value=_build_status), _iframe_html, _file_browser_update,
1080
  gr.update(value=owner_to_use), gr.update(value=space_to_use),
1081
  _changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
1082
- gr.update(value=_formatted_md), gr.update(value=_detected_preview_val), _download_btn_update, gr.update() # Keep runtime status
1083
  )
1084
 
1085
  sdk_built, file_list, err_list = get_space_repository_info(hf_api_key_ui, space_to_use, owner_to_use)
@@ -1207,7 +1212,7 @@ def handle_refresh_space_status(hf_api_key_ui, ui_owner_name, ui_space_name):
1207
  status_details, err = get_space_runtime_status(hf_api_key_ui, ui_space_name, ui_owner_name)
1208
  repo_info = None
1209
  repo_info_err = None
1210
- if not err: # Only fetch repo info if runtime status succeeded (implies space exists)
1211
  sdk, file_list, repo_info_err = get_space_repository_info(hf_api_key_ui, ui_space_name, ui_owner_name)
1212
  if not repo_info_err:
1213
  repo_info = {"sdk": sdk, "file_count": len(file_list) if file_list is not None else 'N/A'}
@@ -1282,13 +1287,12 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
1282
  return "Duplicate Error: Target Owner and Target Space Name are required.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1283
  if "/" in target_space_name:
1284
  return "Duplicate Error: Target Space Name should not contain '/'. Use Target Owner field for the owner part.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1285
- global parsed_code_blocks_state_cache
1286
 
1287
  source_repo_id = f"{source_owner}/{source_space_name}"
1288
  target_repo_id = f"{target_owner}/{target_space_name}"
1289
 
1290
  status_msg = f"Attempting to duplicate `{source_repo_id}` to `{target_repo_id}`..."
1291
- # Yield initial status
1292
  yield status_msg, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1293
 
1294
 
@@ -1296,7 +1300,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
1296
  status_msg = f"Duplication Result: {result_message}"
1297
 
1298
  _status_reload = f"{status_msg} | Attempting to load the new Space [{target_repo_id}]..."
1299
- yield gr.update(value=_status_reload) # Update status
1300
 
1301
  new_owner = target_owner
1302
  new_space_name = target_space_name
@@ -1305,7 +1309,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
1305
 
1306
  if err_list:
1307
  reload_error = f"Error reloading file list after duplication: {err_list}"
1308
-
1309
  parsed_code_blocks_state_cache = []
1310
  _file_browser_update = gr.update(visible=False, choices=[], value=None)
1311
  _iframe_html_update = gr.update(value=None, visible=False)
@@ -1317,6 +1321,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
1317
  is_binary = lang == "binary" or (err_get is not None)
1318
  code = f"[Error loading content: {err_get}]" if err_get else (content or "")
1319
  loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
 
1320
  parsed_code_blocks_state_cache = loaded_files
1321
 
1322
  _file_browser_update = gr.update(visible=True, choices=sorted([f["filename"] for f in parsed_code_blocks_state_cache if not f.get("is_structure_block")] or []), value=None)
@@ -1382,6 +1387,8 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
1382
  owner_name_input = gr.Textbox(label="HF Owner Name", placeholder="e.g., your-username")
1383
  space_name_input = gr.Textbox(label="HF Space Name", value="")
1384
  load_space_button = gr.Button("🔄 Load Existing Space", variant="secondary")
 
 
1385
 
1386
  with gr.Accordion("🤖 AI Model Settings", open=True):
1387
  available_providers = get_available_providers()
@@ -1494,7 +1501,7 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
1494
  confirm_outputs = [
1495
  status_output, formatted_space_output_display, detected_files_preview, download_button,
1496
  confirm_accordion, confirm_button, cancel_button, changeset_state, changeset_display,
1497
- owner_name_input, space_name_input, file_browser_dropdown, space_iframe_display, space_runtime_status_display # Added outputs for potential state changes
1498
  ]
1499
  confirm_button.click(handle_confirm_changes, inputs=confirm_inputs, outputs=confirm_outputs)
1500
 
@@ -1510,7 +1517,7 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
1510
  space_iframe_display, download_button, build_status_display,
1511
  edit_status_display, space_runtime_status_display,
1512
  changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
1513
- list_spaces_display # Added outputs for List Spaces
1514
  ]
1515
  load_space_button.click(
1516
  fn=handle_load_existing_space,
@@ -1518,6 +1525,9 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
1518
  outputs=load_space_outputs
1519
  )
1520
 
 
 
 
1521
  build_outputs = [
1522
  build_status_display, space_iframe_display, file_browser_dropdown,
1523
  owner_name_input, space_name_input,
@@ -1557,4 +1567,4 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
1557
 
1558
 
1559
  if __name__ == "__main__":
1560
- demo.launch(debug=False, mcp_server=True)
 
16
  parse_markdown as build_logic_parse_markdown,
17
  delete_space_file as build_logic_delete_space_file,
18
  get_space_runtime_status,
19
+ apply_staged_file_changes,
20
  duplicate_space as build_logic_duplicate_space,
21
  list_user_spaces as build_logic_list_user_spaces,
22
  build_logic_set_space_privacy,
23
  build_logic_delete_space,
24
  build_logic_create_pull_request,
25
  build_logic_add_comment,
26
+ build_logic_like_space,
27
+ build_logic_create_space # Added back for manual build flow
28
  )
29
 
30
  from model_logic import (
 
111
  'rb': 'ruby', 'php': 'php', 'go': 'go', 'rs': 'rust', 'swift': 'swift', 'kt': 'kotlin', 'kts': 'kotlin',
112
  'sql': 'sql', 'dockerfile': 'docker', 'tf': 'terraform', 'hcl': 'terraform',
113
  'txt': 'plaintext', 'log': 'plaintext', 'ini': 'ini', 'conf': 'plaintext', 'cfg': 'plaintext',
114
+ 'csv': 'plaintext', 'tsv': 'tsv', 'err': 'plaintext',
115
  '.env': 'plaintext', '.gitignore': 'plaintext', '.npmrc': 'plaintext', '.gitattributes': 'plaintext',
116
  'makefile': 'makefile',
117
  }
 
355
  print(f"Staged DELETE_SPACE action for {hf_owner_name}/{hf_repo_name}")
356
 
357
  elif command == "CREATE_PR" and args:
 
358
  target_repo_id = None
359
  title = ""
360
  body = ""
361
  try:
362
  target_repo_id = args[0]
363
+ arg_string = shlex.join(args[1:])
364
 
365
  title_match = re.search(r'--title\s+"([^"]*)"', arg_string)
366
  if title_match: title = title_match.group(1)
 
370
  if body_match: body = body_match.group(1)
371
  else: print("Warning: CREATE_PR missing --body argument.")
372
 
373
+ if target_repo_id and title:
374
  changeset.append({"type": "CREATE_PR", "source_repo_id": f"{hf_owner_name}/{hf_repo_name}", "target_repo_id": target_repo_id, "title": title, "body": body})
375
  print(f"Staged CREATE_PR action to {target_repo_id}")
376
  else:
 
382
 
383
 
384
  elif command == "ADD_COMMENT" and args:
 
385
  comment_text = None
386
  try:
 
 
387
  comment_string = shlex.join(args)
388
+ comment_match = re.match(r'^"([^"]*)"$', comment_string)
389
  if comment_match: comment_text = comment_match.group(1)
390
  else:
 
391
  comment_text = " ".join(args)
392
  print(f"Warning: ADD_COMMENT argument was not a single quoted string, using raw args: '{comment_text}'")
393
 
 
404
 
405
 
406
  elif command == "LIKE_SPACE":
 
407
  if hf_owner_name and hf_repo_name:
408
  changeset.append({"type": "LIKE_SPACE", "repo_id": f"{hf_owner_name}/{hf_repo_name}"})
409
  print(f"Staged LIKE_SPACE action on {hf_owner_name}/{hf_repo_name}")
 
411
  print(f"Warning: Cannot stage LIKE_SPACE action, no Space currently loaded.")
412
  changeset.append({"type": "Error", "message": "Cannot like space, no Space currently loaded."})
413
 
 
 
 
414
  for file_info in ai_proposed_files_list:
415
  filename = file_info["path"]
416
  proposed_content = file_info["content"]
 
448
  for change in space_actions:
449
  if change["type"] == "CREATE_SPACE":
450
  md_summary.append(f"- **🚀 Create New Space:** `{change.get('repo_id', '...')}` (SDK: {change.get('sdk', 'gradio')}, Private: {change.get('private', False)})")
451
+ elif change["type"] == "DUPLICATE_SPACE":
452
+ md_summary.append(f"- **📂 Duplicate Space:** From `{change.get('source_repo_id', '...')}` to `{change.get('target_repo_id', '...')}` (Private: {change.get('private', False)})")
453
  elif change["type"] == "SET_PRIVACY":
454
  md_summary.append(f"- **🔒 Set Privacy:** Set `{change.get('repo_id', '...')}` to `private={change.get('private', False)}`")
455
  elif change["type"] == "DELETE_SPACE":
 
460
  if change.get('body'): md_summary.append(f" - Body: `{change.get('body', '...')}`")
461
  elif change["type"] == "ADD_COMMENT":
462
  md_summary.append(f"- **💬 Add Comment:** On `{change.get('repo_id', '...')}`")
463
+ md_summary.append(f" - Comment: `{change.get('comment', '...')[:100]}{'...' if len(change.get('comment', '')) > 100 else ''}`")
464
  elif change["type"] == "LIKE_SPACE":
465
  md_summary.append(f"- **👍 Like Space:** `{change.get('repo_id', '...')}`")
466
  elif change["type"] == "Error":
 
592
  global parsed_code_blocks_state_cache
593
 
594
  _status = "Applying changes..."
 
595
  yield _status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="*Applying changes...*")
596
+ # Dummy yields for components that might be updated later in the generator
597
+ yield gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
598
 
599
  if not changeset:
600
  return "No changes to apply.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="No changes were staged.")
601
 
 
602
  first_action = changeset[0] if changeset else None
603
  is_exclusive_duplicate = first_action and first_action.get('type') == 'DUPLICATE_SPACE'
604
  is_exclusive_delete = first_action and first_action.get('type') == 'DELETE_SPACE'
 
613
  target_repo_id=change["target_repo_id"],
614
  private=change.get("private", False)
615
  )
 
616
  _status_reload = f"{status_message} | Attempting to load the new Space [{change['target_repo_id']}]..."
617
+ yield gr.update(value=_status_reload)
618
 
 
619
  target_repo_id = change['target_repo_id']
620
  new_owner, new_space_name = target_repo_id.split('/', 1) if '/' in target_repo_id else (None, target_repo_id)
621
 
 
622
  if not new_owner:
623
  token, token_err = build_logic_get_api_token(hf_api_key)
624
  if token_err:
625
  print(f"Error getting token for new owner determination: {token_err}")
626
+ new_owner = None
627
  else:
628
  try: user_info = build_logic_whoami(token=token); new_owner = user_info.get('name')
629
  except Exception as e:
 
631
  new_owner = None
632
 
633
 
 
634
  if new_owner and new_space_name:
635
  sdk, file_list, err_list = get_space_repository_info(hf_api_key, new_space_name, new_owner)
636
  if err_list:
 
661
  else:
662
  iframe_update = gr.update(value=None, visible=False)
663
 
 
664
  runtime_status_md = handle_refresh_space_status(hf_api_key, new_owner, new_space_name)
665
 
 
666
  yield (
667
+ gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
668
+ gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
669
+ [], gr.update(value="*No changes proposed.*"),
670
+ owner_update, space_update, file_browser_update, iframe_update,
671
+ gr.update(value=runtime_status_md)
672
  )
673
 
674
  else:
675
  reload_error = "Cannot load new Space state: Owner or Space Name missing after duplication."
676
+ _formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
677
  final_overall_status = status_message + f" | Reload Status: {reload_error}"
 
678
  yield (
679
  gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
680
  gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
681
  [], gr.update(value="*No changes proposed.*"),
682
  gr.update(), gr.update(), gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False),
683
+ gr.update(value="*Runtime status unavailable for new space.*")
684
  )
685
 
 
686
  else:
 
687
  final_overall_status = "Duplicate Action Error: Invalid duplicate changeset format."
 
688
  _formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
689
  yield (
690
  gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
 
695
 
696
  elif is_exclusive_delete:
697
  change = first_action
 
698
  delete_owner = change.get('owner') or owner_name
699
  delete_space = change.get('space_name') or space_name
700
  delete_repo_id_target = f"{delete_owner}/{delete_space}" if delete_owner and delete_space else None
 
706
  else:
707
  status_message = build_logic_delete_space(hf_api_key, delete_owner, delete_space)
708
  final_overall_status = status_message
 
709
  if "Successfully" in status_message:
710
+ global parsed_code_blocks_state_cache
711
  parsed_code_blocks_state_cache = []
712
+ _formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
713
  owner_update = gr.update(value="")
714
  space_update = gr.update(value="")
715
  file_browser_update = gr.update(visible=False, choices=[], value=None)
716
  iframe_update = gr.update(value=None, visible=False)
717
  runtime_status_update = gr.update(value="*Space deleted, runtime status unavailable.*")
718
  else:
 
719
  _formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
720
  owner_update = gr.update()
721
  space_update = gr.update()
722
  file_browser_update = gr.update()
723
  iframe_update = gr.update()
 
 
724
  runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
725
 
726
 
 
733
  )
734
 
735
 
736
+ else:
 
737
  action_status_messages = []
738
  file_change_operations = []
739
 
 
740
  for change in changeset:
741
  try:
742
  if change['type'] == 'CREATE_SPACE':
743
  repo_id_to_create = change.get('repo_id')
744
  if repo_id_to_create:
745
+ msg = build_logic_create_space(hf_api_key, repo_id_to_create.split('/')[1], repo_id_to_create.split('/')[0] if '/' in repo_id_to_create else None, change.get('sdk', 'gradio'), "", change.get('private', False)) # Note: Manual create requires markdown, passing empty string
746
  action_status_messages.append(f"CREATE_SPACE: {msg}")
747
+ # If creation was successful, potentially update the UI fields? No, AI might create a *different* space.
748
+ # Just report success/failure. The AI can propose loading it next.
749
  else:
750
  action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
751
  elif change['type'] == 'SET_PRIVACY':
752
+ repo_id_to_set_privacy = change.get('repo_id') or f"{owner_name}/{space_name}"
753
+ if repo_id_to_set_privacy and owner_name and space_name:
754
  msg = build_logic_set_space_privacy(hf_api_key, repo_id_to_set_privacy, change['private'])
755
  action_status_messages.append(f"SET_PRIVACY: {msg}")
756
+ else:
757
  action_status_messages.append("SET_PRIVACY Error: Cannot set privacy, no Space currently loaded.")
758
  elif change['type'] == 'CREATE_PR':
 
759
  source_repo = f"{owner_name}/{space_name}" if owner_name and space_name else None
760
  if source_repo and change.get('target_repo_id') and change.get('title'):
761
  msg = build_logic_create_pull_request(hf_api_key, source_repo, change['target_repo_id'], change['title'], change.get('body', ''))
762
  action_status_messages.append(f"CREATE_PR: {msg}")
763
  else:
764
+ action_status_messages.append("CREATE_PR Error: Source/Target repo, title, or body missing or no Space loaded.")
765
  elif change['type'] == 'ADD_COMMENT':
766
  repo_id_to_comment = change.get('repo_id') or f"{owner_name}/{space_name}"
767
  if repo_id_to_comment and owner_name and space_name and change.get('comment'):
 
779
  elif change['type'] == 'Error':
780
  action_status_messages.append(f"Staging Error: {change.get('message', 'Unknown error')}")
781
 
 
782
  elif change['type'] in ['CREATE_FILE', 'UPDATE_FILE', 'DELETE_FILE']:
783
  file_change_operations.append(change)
784
 
 
788
  import traceback
789
  traceback.print_exc()
790
 
 
 
791
  file_commit_status = ""
792
  if file_change_operations:
793
  if owner_name and space_name:
 
795
  else:
796
  file_commit_status = "File Commit Error: Cannot commit file changes, no Space currently loaded."
797
  action_status_messages.append(file_commit_status)
798
+ else:
799
+ file_commit_status = "No file changes (create/update/delete) to commit."
800
 
801
 
 
802
  final_overall_status = " | ".join(action_status_messages)
803
  if file_commit_status and file_commit_status != "No file changes (create/update/delete) to commit.":
804
  if final_overall_status: final_overall_status += " | "
805
  final_overall_status += file_commit_status
806
  if not final_overall_status: final_overall_status = "No operations were applied."
807
 
 
808
  _status_reload = f"{final_overall_status} | Reloading Space state..."
809
+ yield gr.update(value=_status_reload)
810
 
811
  refreshed_file_list = []
812
  reload_error = None
 
837
  else:
838
  iframe_update = gr.update(value=None, visible=False)
839
 
 
840
  runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
841
 
842
 
 
881
  selected_value = default_model if default_model in models else (models[0] if models else None)
882
  return gr.update(choices=models, value=selected_value)
883
 
884
+ def handle_detect_user(hf_api_key_ui, owner_name_ui):
885
+ _status = "Detecting user from token..."
886
+ # Yield initial state to show loading status and clear potentially outdated owner/spaces list
887
+ yield gr.update(value=_status), gr.update(value=""), gr.update(value="*Listing spaces...*")
888
+
889
+ token, token_err = build_logic_get_api_token(hf_api_key_ui)
890
+ if token_err:
891
+ _status = f"Detection Error: {token_err}"
892
+ yield gr.update(value=_status), gr.update(), gr.update(value="*Could not list spaces due to token error.*")
893
+ return
894
+
895
+ try:
896
+ user_info = build_logic_whoami(token=token)
897
+ owner_name = user_info.get('name')
898
+ if not owner_name: raise Exception("Could not find user name from token.")
899
+ _status = f"User detected: {owner_name}"
900
+ owner_update = gr.update(value=owner_name)
901
+
902
+ # Immediately trigger listing spaces using the detected owner
903
+ list_spaces_md, list_err = build_logic_list_user_spaces(hf_api_key=token, owner=owner_name)
904
+ if list_err:
905
+ list_spaces_update = gr.update(value=f"List Spaces Error: {list_err}")
906
+ _status += f" | List Spaces Error: {list_err}"
907
+ else:
908
+ list_spaces_update = gr.update(value=list_spaces_md)
909
+
910
+ yield gr.update(value=_status), owner_update, list_spaces_update
911
+
912
+ except Exception as e:
913
+ _status = f"Detection Error: Could not detect user from token: {e}"
914
+ print(f"Error in handle_detect_user: {e}")
915
+ import traceback
916
+ traceback.print_exc()
917
+ yield gr.update(value=_status), gr.update(), gr.update(value="*Could not list spaces due to user detection error.*")
918
+
919
+
920
  def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
921
  global parsed_code_blocks_state_cache
922
  _formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
 
925
  _changeset_clear = []
926
  _changeset_summary_clear = "*No changes proposed.*"
927
  _confirm_ui_hidden = gr.update(visible=False)
928
+ _list_spaces_display_clear = gr.update() # Don't clear list spaces display on load, it's controlled by its own button
929
+ _target_owner_clear, _target_space_clear, _target_private_clear = gr.update(value=""), gr.update(value=""), gr.update(value=False)
930
 
931
 
932
  yield (
 
935
  _iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
936
  gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
937
  _changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
938
+ _list_spaces_display_clear, _target_owner_clear, _target_space_clear, _target_private_clear
939
  )
940
 
941
  owner_to_use = ui_owner_name
 
973
  gr.update(visible=False, choices=[], value=None),
974
  gr.update(), gr.update(),
975
  gr.update(value=None, visible=False),
976
+ _download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
977
  )
978
  return
979
 
 
1007
  file_browser_update,
1008
  gr.update(), gr.update(),
1009
  iframe_update,
1010
+ _download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1011
  )
1012
 
1013
  def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, is_private_ui, formatted_markdown_content):
 
1042
  yield gr.update(value=_build_status),
1043
  return
1044
 
 
 
 
1045
  token, token_err = build_logic_get_api_token(hf_api_key_ui)
1046
  if token_err:
1047
  _build_status = f"Build Error: API Token Error: {token_err}"
 
1052
 
1053
  build_status_messages = []
1054
  try:
 
1055
  api.create_repo(repo_id=repo_id_target, repo_type="space", space_sdk=space_sdk_select, private=is_private_ui, exist_ok=True)
1056
  build_status_messages.append(f"CREATE_SPACE: Successfully created or ensured space [{repo_id_target}](https://huggingface.co/spaces/{repo_id_target}) exists.")
1057
  except HfHubHTTPError as e_http:
1058
  build_status_messages.append(f"CREATE_SPACE HTTP Error ({e_http.response.status_code if e_http.response else 'N/A'}): {e_http.response.text if e_http.response else str(e_http)}. Check logs.")
1059
  if e_http.response and e_http.response.status_code in (401, 403):
1060
+ _build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), return
1061
  except Exception as e:
1062
  build_status_messages.append(f"CREATE_SPACE Error: {e}")
1063
+ _build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), return
1064
 
1065
 
 
1066
  file_changes_only_changeset = [{"type": "CREATE_FILE", "path": f["path"], "content": f["content"], "lang": _infer_lang_from_filename(f["path"])} for f in proposed_files_list]
1067
  if file_changes_only_changeset:
1068
  file_commit_status = apply_staged_file_changes(hf_api_key_ui, ui_owner_name_part, ui_space_name_part, file_changes_only_changeset)
 
1084
  gr.update(value=_build_status), _iframe_html, _file_browser_update,
1085
  gr.update(value=owner_to_use), gr.update(value=space_to_use),
1086
  _changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
1087
+ gr.update(value=_formatted_md), gr.update(value=_detected_preview_val), _download_btn_update, gr.update()
1088
  )
1089
 
1090
  sdk_built, file_list, err_list = get_space_repository_info(hf_api_key_ui, space_to_use, owner_to_use)
 
1212
  status_details, err = get_space_runtime_status(hf_api_key_ui, ui_space_name, ui_owner_name)
1213
  repo_info = None
1214
  repo_info_err = None
1215
+ if not err:
1216
  sdk, file_list, repo_info_err = get_space_repository_info(hf_api_key_ui, ui_space_name, ui_owner_name)
1217
  if not repo_info_err:
1218
  repo_info = {"sdk": sdk, "file_count": len(file_list) if file_list is not None else 'N/A'}
 
1287
  return "Duplicate Error: Target Owner and Target Space Name are required.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1288
  if "/" in target_space_name:
1289
  return "Duplicate Error: Target Space Name should not contain '/'. Use Target Owner field for the owner part.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1290
+
1291
 
1292
  source_repo_id = f"{source_owner}/{source_space_name}"
1293
  target_repo_id = f"{target_owner}/{target_space_name}"
1294
 
1295
  status_msg = f"Attempting to duplicate `{source_repo_id}` to `{target_repo_id}`..."
 
1296
  yield status_msg, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
1297
 
1298
 
 
1300
  status_msg = f"Duplication Result: {result_message}"
1301
 
1302
  _status_reload = f"{status_msg} | Attempting to load the new Space [{target_repo_id}]..."
1303
+ yield gr.update(value=_status_reload)
1304
 
1305
  new_owner = target_owner
1306
  new_space_name = target_space_name
 
1309
 
1310
  if err_list:
1311
  reload_error = f"Error reloading file list after duplication: {err_list}"
1312
+ global parsed_code_blocks_state_cache
1313
  parsed_code_blocks_state_cache = []
1314
  _file_browser_update = gr.update(visible=False, choices=[], value=None)
1315
  _iframe_html_update = gr.update(value=None, visible=False)
 
1321
  is_binary = lang == "binary" or (err_get is not None)
1322
  code = f"[Error loading content: {err_get}]" if err_get else (content or "")
1323
  loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
1324
+ global parsed_code_blocks_state_cache
1325
  parsed_code_blocks_state_cache = loaded_files
1326
 
1327
  _file_browser_update = gr.update(visible=True, choices=sorted([f["filename"] for f in parsed_code_blocks_state_cache if not f.get("is_structure_block")] or []), value=None)
 
1387
  owner_name_input = gr.Textbox(label="HF Owner Name", placeholder="e.g., your-username")
1388
  space_name_input = gr.Textbox(label="HF Space Name", value="")
1389
  load_space_button = gr.Button("🔄 Load Existing Space", variant="secondary")
1390
+ detect_user_button = gr.Button("👤 Detect User from Token", variant="secondary")
1391
+
1392
 
1393
  with gr.Accordion("🤖 AI Model Settings", open=True):
1394
  available_providers = get_available_providers()
 
1501
  confirm_outputs = [
1502
  status_output, formatted_space_output_display, detected_files_preview, download_button,
1503
  confirm_accordion, confirm_button, cancel_button, changeset_state, changeset_display,
1504
+ owner_name_input, space_name_input, file_browser_dropdown, space_iframe_display, space_runtime_status_display
1505
  ]
1506
  confirm_button.click(handle_confirm_changes, inputs=confirm_inputs, outputs=confirm_outputs)
1507
 
 
1517
  space_iframe_display, download_button, build_status_display,
1518
  edit_status_display, space_runtime_status_display,
1519
  changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
1520
+ list_spaces_display, target_owner_input, target_space_name_input, target_private_checkbox
1521
  ]
1522
  load_space_button.click(
1523
  fn=handle_load_existing_space,
 
1525
  outputs=load_space_outputs
1526
  )
1527
 
1528
+ detect_user_outputs = [status_output, owner_name_input, list_spaces_display]
1529
+ detect_user_button.click(fn=handle_detect_user, inputs=[hf_api_key_input, owner_name_input], outputs=detect_user_outputs)
1530
+
1531
  build_outputs = [
1532
  build_status_display, space_iframe_display, file_browser_dropdown,
1533
  owner_name_input, space_name_input,
 
1567
 
1568
 
1569
  if __name__ == "__main__":
1570
+ demo.launch(debug=False, mcp_server=True)