naman1102 commited on
Commit
9030be0
Β·
1 Parent(s): b3607a6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -40
app.py CHANGED
@@ -669,6 +669,14 @@ def create_ui() -> gr.Blocks:
669
  wrap=True,
670
  interactive=False
671
  )
 
 
 
 
 
 
 
 
672
 
673
  gr.Markdown("### πŸ“‹ All Analysis Results")
674
  gr.Markdown("πŸ’‘ **Click repository names to visit them on Hugging Face**")
@@ -678,6 +686,29 @@ def create_ui() -> gr.Blocks:
678
  wrap=True,
679
  interactive=False
680
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
 
682
  # --- Chatbot Tab ---
683
  with gr.TabItem("πŸ€– AI Assistant", id="chatbot_tab"):
@@ -839,37 +870,102 @@ def create_ui() -> gr.Blocks:
839
 
840
  return history, "πŸ’¬ Conversation continuing...", "", "", [], 0, pd.DataFrame(), gr.update()
841
 
842
- def handle_repo_click(evt: gr.SelectData, df_data) -> str:
843
- """Handle direct repository clicks - open HF space directly."""
 
 
 
 
844
  if evt is None:
845
- return ""
846
 
847
  try:
 
848
  row_idx = evt.index[0]
849
  col_idx = evt.index[1]
 
850
 
851
- # Only handle clicks on the repository name column (column 0)
852
- if col_idx == 0 and isinstance(df_data, pd.DataFrame) and not df_data.empty and row_idx < len(df_data):
853
- repo_id = df_data.iloc[row_idx, 0]
 
 
 
 
 
 
 
 
854
 
855
- if repo_id and str(repo_id).strip() and str(repo_id).strip() != 'nan':
856
- hf_url = f"https://huggingface.co/spaces/{str(repo_id).strip()}"
857
- logger.info(f"Opening repository: {hf_url}")
858
- return hf_url
 
 
 
859
  except Exception as e:
860
- logger.error(f"Error handling repository click: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
 
 
 
 
 
 
 
 
 
 
 
862
  return ""
863
 
864
- def handle_extract_and_analyze(history: List[Dict[str, str]]) -> Tuple[str, str, str, List[str], int, pd.DataFrame, Any, pd.DataFrame, str, Any]:
865
  """Extract keywords from chat, search repositories, and immediately start analysis."""
866
  if not history:
867
- return "❌ No conversation to extract from.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
868
 
869
  # Convert the full, valid history for the extraction logic
870
  tuple_history = convert_messages_to_tuples(history)
871
  if not tuple_history:
872
- return "❌ No completed conversations to analyze.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
873
 
874
  # Get raw keywords string from the LLM
875
  raw_keywords_str = extract_keywords_from_conversation(tuple_history)
@@ -879,7 +975,7 @@ def create_ui() -> gr.Blocks:
879
  cleaned_keywords = [kw.strip() for kw in cleaned_keywords if kw.strip()]
880
 
881
  if not cleaned_keywords:
882
- return f"❌ Could not extract valid keywords. Raw output: '{raw_keywords_str}'", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
883
 
884
  # Join them into a clean, comma-separated string
885
  final_keywords_str = ", ".join(cleaned_keywords)
@@ -895,23 +991,23 @@ def create_ui() -> gr.Blocks:
895
  unique_repo_ids = list(dict.fromkeys(repo_ids))
896
 
897
  if not unique_repo_ids:
898
- return f"❌ No repositories found for keywords: {final_keywords_str}", final_keywords_str, user_requirements, [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
899
 
900
  write_repos_to_csv(unique_repo_ids)
901
  df = format_dataframe_for_display(read_csv_to_dataframe())
902
 
903
  # Immediately start analysis
904
  try:
905
- analyzed_df, analysis_status, top_repos, top_section_update = handle_analyze_all_repos(unique_repo_ids, user_requirements)
906
 
907
  chat_status = f"πŸŽ‰ Extracted keywords β†’ Found {len(unique_repo_ids)} repositories β†’ Analysis complete!"
908
 
909
- return chat_status, final_keywords_str, user_requirements, unique_repo_ids, 0, analyzed_df, gr.update(selected="analysis_tab"), top_repos, analysis_status, top_section_update
910
 
911
  except Exception as e:
912
  logger.error(f"Error during extract and analyze: {e}")
913
  error_status = f"βœ… Found {len(unique_repo_ids)} repositories, but analysis failed: {e}"
914
- return error_status, final_keywords_str, user_requirements, unique_repo_ids, 0, df, gr.update(selected="analysis_tab"), pd.DataFrame(), "", gr.update(visible=False)
915
 
916
  def extract_user_requirements_from_chat(history: List[Dict[str, str]]) -> str:
917
  """Extract user requirements from chatbot conversation."""
@@ -930,10 +1026,10 @@ def create_ui() -> gr.Blocks:
930
  requirements = "\n".join([f"- {msg}" for msg in user_messages if msg.strip()])
931
  return requirements
932
 
933
- def handle_analyze_all_repos(repo_ids: List[str], user_requirements: str, progress=gr.Progress()) -> Tuple[pd.DataFrame, str, pd.DataFrame, Any]:
934
  """Analyzes all repositories in the CSV file with progress tracking."""
935
  if not repo_ids:
936
- return pd.DataFrame(), "Status: No repositories to analyze. Please submit repo IDs first.", pd.DataFrame(), gr.update(visible=False)
937
 
938
  total_repos = len(repo_ids)
939
 
@@ -1030,6 +1126,10 @@ def create_ui() -> gr.Blocks:
1030
  # Get top 3 most relevant repositories using full data
1031
  top_repos = get_top_relevant_repos(updated_df, user_requirements, top_n=3)
1032
 
 
 
 
 
1033
  # Final status with detailed breakdown
1034
  final_status = f"πŸŽ‰ Batch Analysis Complete!\nβœ… Successful: {successful_analyses}/{total_repos}\n❌ Failed: {failed_analyses}/{total_repos}"
1035
  if csv_update_failures > 0:
@@ -1043,12 +1143,12 @@ def create_ui() -> gr.Blocks:
1043
  show_top_section = gr.update(visible=not top_repos.empty)
1044
 
1045
  logger.info(f"Batch analysis completed: {successful_analyses} successful, {failed_analyses} failed, {csv_update_failures} CSV update issues")
1046
- return format_dataframe_for_display(analyzed_df), final_status, format_dataframe_for_display(top_repos), show_top_section
1047
 
1048
  except Exception as e:
1049
  logger.error(f"Error in batch analysis: {e}")
1050
  error_status = f"❌ Batch analysis failed: {e}"
1051
- return format_dataframe_for_display(read_csv_to_dataframe()), error_status, pd.DataFrame(), gr.update(visible=False)
1052
 
1053
  def handle_reset_everything() -> Tuple[List[str], int, str, pd.DataFrame, pd.DataFrame, Any, List[Dict[str, str]], str, str, str]:
1054
  """Reset everything to initial state - clear all data, CSV, and UI components."""
@@ -1122,9 +1222,9 @@ def create_ui() -> gr.Blocks:
1122
  outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
1123
  ).then(
1124
  # If auto_analyze is enabled and we got repos, start analysis automatically
1125
- fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False)),
1126
  inputs=[repo_ids_state, user_requirements_state, status_box_input],
1127
- outputs=[df_output, status_box_input, top_repos_df, top_repos_section]
1128
  )
1129
 
1130
  # Smart Submit Button (same behavior as enter)
@@ -1134,9 +1234,9 @@ def create_ui() -> gr.Blocks:
1134
  outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
1135
  ).then(
1136
  # If auto_analyze is enabled and we got repos, start analysis automatically
1137
- fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False)),
1138
  inputs=[repo_ids_state, user_requirements_state, status_box_input],
1139
- outputs=[df_output, status_box_input, top_repos_df, top_repos_section]
1140
  )
1141
 
1142
  # Auto-analyze checkbox toggle
@@ -1150,7 +1250,7 @@ def create_ui() -> gr.Blocks:
1150
  analyze_all_btn.click(
1151
  fn=handle_analyze_all_repos,
1152
  inputs=[repo_ids_state, user_requirements_state],
1153
- outputs=[df_output, status_box_analysis, top_repos_df, top_repos_section]
1154
  )
1155
 
1156
  # Chatbot with Auto-extraction and Auto-search
@@ -1169,9 +1269,9 @@ def create_ui() -> gr.Blocks:
1169
  outputs=[current_requirements_display]
1170
  ).then(
1171
  # If we got repos from chatbot, auto-analyze them
1172
- fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False)),
1173
  inputs=[repo_ids_state, user_requirements_state],
1174
- outputs=[df_output, chat_status, top_repos_df, top_repos_section]
1175
  )
1176
 
1177
  send_btn.click(
@@ -1189,16 +1289,16 @@ def create_ui() -> gr.Blocks:
1189
  outputs=[current_requirements_display]
1190
  ).then(
1191
  # If we got repos from chatbot, auto-analyze them
1192
- fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False)),
1193
  inputs=[repo_ids_state, user_requirements_state],
1194
- outputs=[df_output, chat_status, top_repos_df, top_repos_section]
1195
  )
1196
 
1197
  # Extract and Analyze Button (one-click solution for chatbot)
1198
  extract_analyze_btn.click(
1199
  fn=handle_extract_and_analyze,
1200
  inputs=[chatbot],
1201
- outputs=[chat_status, extracted_keywords_output, user_requirements_state, repo_ids_state, current_repo_idx_state, df_output, tabs, top_repos_df, status_box_analysis, top_repos_section]
1202
  ).then(
1203
  # Update requirements display when they change
1204
  fn=lambda req: req if req.strip() else "No specific requirements extracted from conversation.",
@@ -1209,19 +1309,44 @@ def create_ui() -> gr.Blocks:
1209
  # Repo Explorer Tab
1210
  setup_repo_explorer_events(repo_components, repo_states)
1211
 
1212
- # Direct Repository Clicks - Open HF Space
1213
  df_output.select(
1214
- fn=handle_repo_click,
1215
  inputs=[df_output],
1216
- outputs=[status_box_input],
1217
- js="(url) => { if(url && url.trim()) { window.open(url, '_blank'); } }"
1218
  )
1219
 
1220
  top_repos_df.select(
1221
- fn=handle_repo_click,
1222
  inputs=[top_repos_df],
1223
- outputs=[status_box_input],
1224
- js="(url) => { if(url && url.trim()) { window.open(url, '_blank'); } }"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1225
  )
1226
 
1227
  # Reset button event
 
669
  wrap=True,
670
  interactive=False
671
  )
672
+
673
+ # Quick links for top repositories
674
+ with gr.Row():
675
+ top_repo_links = gr.HTML(
676
+ value="",
677
+ label="πŸ”— Quick Links",
678
+ visible=False
679
+ )
680
 
681
  gr.Markdown("### πŸ“‹ All Analysis Results")
682
  gr.Markdown("πŸ’‘ **Click repository names to visit them on Hugging Face**")
 
686
  wrap=True,
687
  interactive=False
688
  )
689
+
690
+ # Modal popup for repository action selection (like in old_app2.py)
691
+ with gr.Row():
692
+ with gr.Column():
693
+ repo_action_modal = gr.Column(visible=False)
694
+ with repo_action_modal:
695
+ gr.Markdown("### πŸ”— Repository Actions")
696
+ selected_repo_display = gr.Textbox(
697
+ label="Selected Repository",
698
+ interactive=False,
699
+ info="Choose what you'd like to do with this repository"
700
+ )
701
+ with gr.Row():
702
+ visit_repo_btn = gr.Button("🌐 Visit Hugging Face Space", variant="primary", size="lg")
703
+ explore_repo_btn = gr.Button("πŸ” Open in Repo Explorer", variant="secondary", size="lg")
704
+ cancel_modal_btn = gr.Button("❌ Cancel", size="lg")
705
+
706
+ # Quick links section for all repositories
707
+ with gr.Row():
708
+ all_repo_links = gr.HTML(
709
+ value="",
710
+ label="πŸ”— Repository Quick Links"
711
+ )
712
 
713
  # --- Chatbot Tab ---
714
  with gr.TabItem("πŸ€– AI Assistant", id="chatbot_tab"):
 
870
 
871
  return history, "πŸ’¬ Conversation continuing...", "", "", [], 0, pd.DataFrame(), gr.update()
872
 
873
+ def handle_dataframe_select(evt: gr.SelectData, df_data) -> Tuple[str, Any, str]:
874
+ """Handle dataframe row selection - show modal for repo ID (column 0) clicks."""
875
+ print(f"DEBUG: Selection event triggered!")
876
+ print(f"DEBUG: evt = {evt}")
877
+ print(f"DEBUG: df_data type = {type(df_data)}")
878
+
879
  if evt is None:
880
+ return "", gr.update(visible=False), ""
881
 
882
  try:
883
+ # Get the selected row and column from the event
884
  row_idx = evt.index[0]
885
  col_idx = evt.index[1]
886
+ print(f"DEBUG: Selected row {row_idx}, column {col_idx}")
887
 
888
+ # Handle pandas DataFrame
889
+ if isinstance(df_data, pd.DataFrame) and not df_data.empty and row_idx < len(df_data):
890
+
891
+ if col_idx == 0: # Repository name column - show action modal
892
+ repo_id = df_data.iloc[row_idx, 0]
893
+ print(f"DEBUG: Extracted repo_id = '{repo_id}'")
894
+
895
+ if repo_id and str(repo_id).strip() and str(repo_id).strip() != 'nan':
896
+ clean_repo_id = str(repo_id).strip()
897
+ logger.info(f"Showing modal for repository: {clean_repo_id}")
898
+ return clean_repo_id, gr.update(visible=True), clean_repo_id
899
 
900
+ # For content columns (1,2,3) and relevance (4), do nothing since full text is shown directly
901
+ else:
902
+ print(f"DEBUG: Clicked on column {col_idx}, full text already shown in table")
903
+ return "", gr.update(visible=False), ""
904
+ else:
905
+ print(f"DEBUG: df_data is not a DataFrame or row_idx {row_idx} out of range")
906
+
907
  except Exception as e:
908
+ print(f"DEBUG: Exception occurred: {e}")
909
+ logger.error(f"Error handling dataframe selection: {e}")
910
+
911
+ return "", gr.update(visible=False), ""
912
+
913
+ def handle_visit_repo(repo_id: str) -> Tuple[Any, str]:
914
+ """Handle visiting the Hugging Face Space for the repository."""
915
+ if repo_id and repo_id.strip():
916
+ hf_url = f"https://huggingface.co/spaces/{repo_id.strip()}"
917
+ logger.info(f"User chose to visit: {hf_url}")
918
+ return gr.update(visible=False), hf_url
919
+ return gr.update(visible=False), ""
920
+
921
+ def handle_explore_repo(selected_repo_id: str) -> Tuple[Any, Any, Any]:
922
+ """Handle navigating to the repo explorer and populate the repo ID."""
923
+ logger.info(f"DEBUG: handle_explore_repo called with selected_repo_id: '{selected_repo_id}'")
924
+
925
+ if selected_repo_id and selected_repo_id.strip() and selected_repo_id.strip() != 'nan':
926
+ clean_repo_id = selected_repo_id.strip()
927
+ return (
928
+ gr.update(visible=False), # close modal
929
+ gr.update(selected="repo_explorer_tab"), # switch tab
930
+ gr.update(value=clean_repo_id) # populate repo explorer input
931
+ )
932
+ else:
933
+ return (
934
+ gr.update(visible=False), # close modal
935
+ gr.update(selected="repo_explorer_tab"), # switch tab
936
+ gr.update() # don't change repo explorer input
937
+ )
938
+
939
+ def handle_cancel_modal() -> Any:
940
+ """Handle closing the modal."""
941
+ return gr.update(visible=False)
942
+
943
+ def generate_repo_links_html(df: pd.DataFrame) -> str:
944
+ """Generate HTML with clickable links for repositories."""
945
+ if df.empty:
946
+ return ""
947
 
948
+ html_links = []
949
+ for idx, row in df.iterrows():
950
+ repo_id = row.get('repo id', '') if hasattr(row, 'get') else row[0]
951
+ if repo_id and str(repo_id).strip() and str(repo_id).strip() != 'nan':
952
+ clean_repo_id = str(repo_id).strip()
953
+ hf_url = f"https://huggingface.co/spaces/{clean_repo_id}"
954
+ html_links.append(f'<a href="{hf_url}" target="_blank" style="display: inline-block; margin: 5px 10px; padding: 8px 16px; background: linear-gradient(45deg, #667eea, #764ba2); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; transition: all 0.3s ease;">{clean_repo_id}</a>')
955
+
956
+ if html_links:
957
+ return f'<div style="margin: 10px 0; padding: 15px; background: rgba(255, 255, 255, 0.1); border-radius: 12px; backdrop-filter: blur(10px);">{"".join(html_links)}</div>'
958
  return ""
959
 
960
+ def handle_extract_and_analyze(history: List[Dict[str, str]]) -> Tuple[str, str, str, List[str], int, pd.DataFrame, Any, pd.DataFrame, str, Any, str, str]:
961
  """Extract keywords from chat, search repositories, and immediately start analysis."""
962
  if not history:
963
+ return "❌ No conversation to extract from.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
964
 
965
  # Convert the full, valid history for the extraction logic
966
  tuple_history = convert_messages_to_tuples(history)
967
  if not tuple_history:
968
+ return "❌ No completed conversations to analyze.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
969
 
970
  # Get raw keywords string from the LLM
971
  raw_keywords_str = extract_keywords_from_conversation(tuple_history)
 
975
  cleaned_keywords = [kw.strip() for kw in cleaned_keywords if kw.strip()]
976
 
977
  if not cleaned_keywords:
978
+ return f"❌ Could not extract valid keywords. Raw output: '{raw_keywords_str}'", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
979
 
980
  # Join them into a clean, comma-separated string
981
  final_keywords_str = ", ".join(cleaned_keywords)
 
991
  unique_repo_ids = list(dict.fromkeys(repo_ids))
992
 
993
  if not unique_repo_ids:
994
+ return f"❌ No repositories found for keywords: {final_keywords_str}", final_keywords_str, user_requirements, [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
995
 
996
  write_repos_to_csv(unique_repo_ids)
997
  df = format_dataframe_for_display(read_csv_to_dataframe())
998
 
999
  # Immediately start analysis
1000
  try:
1001
+ analyzed_df, analysis_status, top_repos, top_section_update, all_links, top_links = handle_analyze_all_repos(unique_repo_ids, user_requirements)
1002
 
1003
  chat_status = f"πŸŽ‰ Extracted keywords β†’ Found {len(unique_repo_ids)} repositories β†’ Analysis complete!"
1004
 
1005
+ return chat_status, final_keywords_str, user_requirements, unique_repo_ids, 0, analyzed_df, gr.update(selected="analysis_tab"), top_repos, analysis_status, top_section_update, all_links, top_links
1006
 
1007
  except Exception as e:
1008
  logger.error(f"Error during extract and analyze: {e}")
1009
  error_status = f"βœ… Found {len(unique_repo_ids)} repositories, but analysis failed: {e}"
1010
+ return error_status, final_keywords_str, user_requirements, unique_repo_ids, 0, df, gr.update(selected="analysis_tab"), pd.DataFrame(), "", gr.update(visible=False), "", ""
1011
 
1012
  def extract_user_requirements_from_chat(history: List[Dict[str, str]]) -> str:
1013
  """Extract user requirements from chatbot conversation."""
 
1026
  requirements = "\n".join([f"- {msg}" for msg in user_messages if msg.strip()])
1027
  return requirements
1028
 
1029
+ def handle_analyze_all_repos(repo_ids: List[str], user_requirements: str, progress=gr.Progress()) -> Tuple[pd.DataFrame, str, pd.DataFrame, Any, str, str]:
1030
  """Analyzes all repositories in the CSV file with progress tracking."""
1031
  if not repo_ids:
1032
+ return pd.DataFrame(), "Status: No repositories to analyze. Please submit repo IDs first.", pd.DataFrame(), gr.update(visible=False), "", ""
1033
 
1034
  total_repos = len(repo_ids)
1035
 
 
1126
  # Get top 3 most relevant repositories using full data
1127
  top_repos = get_top_relevant_repos(updated_df, user_requirements, top_n=3)
1128
 
1129
+ # Generate HTML links for repositories
1130
+ all_links_html = generate_repo_links_html(analyzed_df)
1131
+ top_links_html = generate_repo_links_html(top_repos) if not top_repos.empty else ""
1132
+
1133
  # Final status with detailed breakdown
1134
  final_status = f"πŸŽ‰ Batch Analysis Complete!\nβœ… Successful: {successful_analyses}/{total_repos}\n❌ Failed: {failed_analyses}/{total_repos}"
1135
  if csv_update_failures > 0:
 
1143
  show_top_section = gr.update(visible=not top_repos.empty)
1144
 
1145
  logger.info(f"Batch analysis completed: {successful_analyses} successful, {failed_analyses} failed, {csv_update_failures} CSV update issues")
1146
+ return format_dataframe_for_display(analyzed_df), final_status, format_dataframe_for_display(top_repos), show_top_section, all_links_html, top_links_html
1147
 
1148
  except Exception as e:
1149
  logger.error(f"Error in batch analysis: {e}")
1150
  error_status = f"❌ Batch analysis failed: {e}"
1151
+ return format_dataframe_for_display(read_csv_to_dataframe()), error_status, pd.DataFrame(), gr.update(visible=False), "", ""
1152
 
1153
  def handle_reset_everything() -> Tuple[List[str], int, str, pd.DataFrame, pd.DataFrame, Any, List[Dict[str, str]], str, str, str]:
1154
  """Reset everything to initial state - clear all data, CSV, and UI components."""
 
1222
  outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
1223
  ).then(
1224
  # If auto_analyze is enabled and we got repos, start analysis automatically
1225
+ fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False), "", ""),
1226
  inputs=[repo_ids_state, user_requirements_state, status_box_input],
1227
+ outputs=[df_output, status_box_input, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
1228
  )
1229
 
1230
  # Smart Submit Button (same behavior as enter)
 
1234
  outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
1235
  ).then(
1236
  # If auto_analyze is enabled and we got repos, start analysis automatically
1237
+ fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False), "", ""),
1238
  inputs=[repo_ids_state, user_requirements_state, status_box_input],
1239
+ outputs=[df_output, status_box_input, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
1240
  )
1241
 
1242
  # Auto-analyze checkbox toggle
 
1250
  analyze_all_btn.click(
1251
  fn=handle_analyze_all_repos,
1252
  inputs=[repo_ids_state, user_requirements_state],
1253
+ outputs=[df_output, status_box_analysis, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
1254
  )
1255
 
1256
  # Chatbot with Auto-extraction and Auto-search
 
1269
  outputs=[current_requirements_display]
1270
  ).then(
1271
  # If we got repos from chatbot, auto-analyze them
1272
+ fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False), "", ""),
1273
  inputs=[repo_ids_state, user_requirements_state],
1274
+ outputs=[df_output, chat_status, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
1275
  )
1276
 
1277
  send_btn.click(
 
1289
  outputs=[current_requirements_display]
1290
  ).then(
1291
  # If we got repos from chatbot, auto-analyze them
1292
+ fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False), "", ""),
1293
  inputs=[repo_ids_state, user_requirements_state],
1294
+ outputs=[df_output, chat_status, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
1295
  )
1296
 
1297
  # Extract and Analyze Button (one-click solution for chatbot)
1298
  extract_analyze_btn.click(
1299
  fn=handle_extract_and_analyze,
1300
  inputs=[chatbot],
1301
+ outputs=[chat_status, extracted_keywords_output, user_requirements_state, repo_ids_state, current_repo_idx_state, df_output, tabs, top_repos_df, status_box_analysis, top_repos_section, all_repo_links, top_repo_links]
1302
  ).then(
1303
  # Update requirements display when they change
1304
  fn=lambda req: req if req.strip() else "No specific requirements extracted from conversation.",
 
1309
  # Repo Explorer Tab
1310
  setup_repo_explorer_events(repo_components, repo_states)
1311
 
1312
+ # Direct Repository Clicks - Show Modal (like old_app2.py)
1313
  df_output.select(
1314
+ fn=handle_dataframe_select,
1315
  inputs=[df_output],
1316
+ outputs=[selected_repo_display, repo_action_modal, selected_repo_id_state]
 
1317
  )
1318
 
1319
  top_repos_df.select(
1320
+ fn=handle_dataframe_select,
1321
  inputs=[top_repos_df],
1322
+ outputs=[selected_repo_display, repo_action_modal, selected_repo_id_state]
1323
+ )
1324
+
1325
+ # Modal button events (like old_app2.py)
1326
+ visit_repo_btn.click(
1327
+ fn=handle_visit_repo,
1328
+ inputs=[selected_repo_display],
1329
+ outputs=[repo_action_modal, selected_repo_display],
1330
+ js="(repo_id) => { if(repo_id && repo_id.trim()) { window.open('https://huggingface.co/spaces/' + repo_id.trim(), '_blank'); } }"
1331
+ )
1332
+ explore_repo_btn.click(
1333
+ fn=handle_explore_repo,
1334
+ inputs=[selected_repo_id_state],
1335
+ outputs=[
1336
+ repo_action_modal,
1337
+ tabs,
1338
+ repo_components["repo_explorer_input"]
1339
+ ],
1340
+ js="""(repo_id) => {
1341
+ console.log('DEBUG: Navigate to repo explorer for:', repo_id);
1342
+ setTimeout(() => {
1343
+ window.scrollTo({top: 0, behavior: 'smooth'});
1344
+ }, 200);
1345
+ }"""
1346
+ )
1347
+ cancel_modal_btn.click(
1348
+ fn=handle_cancel_modal,
1349
+ outputs=[repo_action_modal]
1350
  )
1351
 
1352
  # Reset button event