File size: 8,395 Bytes
cafc4a3
 
 
 
 
36c9099
cafc4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36c9099
 
cafc4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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