import streamlit as st import tempfile import subprocess import os from streamlit_ace import st_ace from utils import save_generated_code_to_new_file # chatではなくutilsからインポート def setup_editor(api_key, mobile=False): """コードエディタとその関連機能をセットアップする""" # ヘッダー st.header("コードエディタ" if not mobile else "エディタ") if st.session_state.current_file: # ファイルの拡張子に基づいて言語を設定 file_ext = os.path.splitext(st.session_state.current_file)[1].lower() lang_map = { ".py": "python", ".js": "javascript", ".html": "html", ".css": "css", ".json": "json", ".txt": "text" } language = lang_map.get(file_ext, "python") # エディタの表示 st.subheader(f"編集中: {st.session_state.current_file}") # モバイル向けの調整 editor_height = 300 if mobile else 500 # AIに編集を任せるモード if st.session_state.ai_edit_mode and st.button("AIに編集を依頼", key="ai_edit_btn"): current_code = st.session_state.files[st.session_state.current_file] instructions = st.session_state.edit_instructions or "コードを改善してください" with st.spinner("AIによるコード編集中..."): # request_ai_editを直接インポートせず、関数を通して呼び出す from chat import request_ai_edit # 関数を使う直前でインポート(循環を避ける) edited_code = request_ai_edit( api_key, current_code, instructions, language ) if edited_code: st.session_state.files[st.session_state.current_file] = edited_code st.success("AIによるコード編集が完了しました") st.session_state.active_tab = "エディタ" # モバイルの場合はエディタタブに戻る st.rerun() # エディタを表示 code = st_ace( value=st.session_state.files[st.session_state.current_file], language=language, theme="monokai", keybinding="vscode", font_size=14 if not mobile else 16, # モバイルでは少し大きく min_lines=10, max_lines=50, height=editor_height, key=f"ace_editor_{st.session_state.current_file}" ) # コードを保存 st.session_state.files[st.session_state.current_file] = code # アクション関連のボタン col1, col2 = st.columns(2) # Python codeの場合、実行ボタンを表示 if language == "python": with col1: if st.button("コードを実行", key="run_code_btn", use_container_width=True): run_python_code(code) # ファイルダウンロード機能 with col2: st.download_button( label="ファイルをダウンロード", data=code, file_name=st.session_state.current_file, key="download_file", use_container_width=True ) else: st.info("サイドバーから既存のファイルを選択するか、新しいファイルを作成してください。") # 簡易ファイル作成ボタン(特にモバイル向け) if st.button("新しいPythonファイルを作成", key="quick_file_create", use_container_width=True): import time filename = f"script_{int(time.time())}.py" st.session_state.files[filename] = "# 新しいPythonスクリプト\n\n# コードを入力してください\n" st.session_state.current_file = filename st.rerun() def run_python_code(code): """Pythonコードを実行して結果を表示する""" with st.spinner("コードを実行中..."): # 一時ファイルを作成してコードを実行 with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as tmp: tmp.write(code.encode()) tmp_name = tmp.name try: # サブプロセスでPythonコードを実行 result = subprocess.run( ["python", tmp_name], capture_output=True, text=True, timeout=10 # 10秒のタイムアウト ) # 実行結果を表示 if result.stdout: st.code(result.stdout, language="text") if result.stderr: st.error(result.stderr) # エラーが発生した場合、自動的にエラー修正モードに切り替える if st.button("エラー修正AIに相談", key="ask_error_fix"): st.session_state.current_mode = "エラー修正モード" error_query = f"次のPythonコードにエラーがあります。修正方法を教えてください。\n\n```python\n{code}\n```\n\nエラーメッセージ:\n```\n{result.stderr}\n```" # チャットにエラー修正用のメッセージを追加 if "messages" not in st.session_state: st.session_state.messages = [] st.session_state.messages.append({"role": "user", "content": error_query}) st.session_state.active_tab = "チャット" # モバイルモードではチャットタブに切り替え st.rerun() except subprocess.TimeoutExpired: st.error("実行がタイムアウトしました(10秒以上かかりました)") except Exception as e: st.error(f"実行中にエラーが発生しました: {str(e)}") finally: # 一時ファイルを削除 if os.path.exists(tmp_name): os.unlink(tmp_name) def save_generated_code_to_new_file(code_text, suggested_name=None): """AIが生成したコードを新しいファイルに保存する""" import re # コードブロックのマークアップを削除 code_pattern = r"```(?:\w+)?\n([\s\S]*?)\n```" match = re.search(code_pattern, code_text) clean_code = match.group(1) if match else code_text # 言語の推測 language = "py" # デフォルトはPython lang_pattern = r"```(\w+)\n" lang_match = re.search(lang_pattern, code_text) if lang_match: detected_lang = lang_match.group(1).lower() lang_mapping = { "python": "py", "javascript": "js", "html": "html", "css": "css", "json": "json", "java": "java", "cpp": "cpp", "c++": "cpp", "c": "c", "go": "go", "rust": "rs", "typescript": "ts" } language = lang_mapping.get(detected_lang, detected_lang) # ファイル名の生成 if not suggested_name: import hashlib import time # タイムスタンプとコードの一部からハッシュを生成 timestamp = str(int(time.time())) code_hash = hashlib.md5(clean_code[:100].encode()).hexdigest()[:6] suggested_name = f"generated_{timestamp}_{code_hash}" # 拡張子が含まれていなければ追加 if "." not in suggested_name: suggested_name = f"{suggested_name}.{language}" # 同名ファイルが存在する場合は連番を付ける base_name, ext = os.path.splitext(suggested_name) counter = 1 file_name = suggested_name while file_name in st.session_state.files: file_name = f"{base_name}_{counter}{ext}" counter += 1 # ファイルを保存 st.session_state.files[file_name] = clean_code st.session_state.current_file = file_name return file_name