1Egyb commited on
Commit
c8e6f64
·
verified ·
1 Parent(s): be3d7cd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -41
app.py CHANGED
@@ -6,6 +6,12 @@ import zipfile
6
  from pathlib import Path
7
  import gradio as gr
8
  from huggingface_hub import InferenceClient
 
 
 
 
 
 
9
 
10
  # --------------------------- الإعدادات ---------------------------
11
  ALLOWED_EXTENSIONS = {
@@ -40,6 +46,7 @@ class ProjectTank:
40
  shutil.copy(f.name, dest)
41
 
42
  self._reload_memory()
 
43
  return f"🚀 تم تحميل {len(self.files_cache)} ملف بنجاح.", list(self.files_cache.keys())
44
 
45
  def _reload_memory(self):
@@ -100,6 +107,50 @@ class ProjectTank:
100
  out_path = tempfile.mktemp()
101
  shutil.make_archive(out_path, 'zip', self.work_dir)
102
  return out_path + ".zip"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  tank = ProjectTank()
105
  client = InferenceClient(api_key=HF_TOKEN) if HF_TOKEN else None
@@ -164,55 +215,131 @@ def apply_and_preview(history):
164
 
165
  # --------------------------- الواجهة ---------------------------
166
 
167
- with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", spacing_size="sm"), title="AI Tank IDE Power") as demo:
168
- gr.Markdown("# 🛡️ AI Tank IDE Pro (V2)\n**بيئة تطوير ذكية مع رؤية كاملة للهيكل**")
169
-
170
- active_file = gr.State()
171
 
172
- with gr.Row():
173
- with gr.Column(scale=1):
174
- upload = gr.UploadButton("📁 ارفع المشروع (ZIP/Files)", file_count="multiple", variant="secondary")
175
- file_list = gr.Dropdown(label="مستكشف الملفات", choices=[])
176
- status = gr.Markdown("🟢 جاهز للعمل")
177
-
178
- with gr.Group():
179
- chat = gr.Chatbot(type="messages", height=450)
180
- msg = gr.Textbox(placeholder="اطلب تعديلاً أو اسأل عن عمل المشروع...", show_label=False)
181
- with gr.Row():
182
- send = gr.Button("إرسال", variant="primary")
183
- apply = gr.Button("⚡ تطبيق التعديلات", variant="stop")
184
-
185
- export_btn = gr.DownloadButton("📥 تصدير المشروع النهائي", variant="secondary")
186
-
187
- with gr.Column(scale=2):
188
- with gr.Tabs():
189
- with gr.TabItem("🌐 المعاينة المباشرة"):
190
- preview = gr.HTML(value="<div style='text-align:center; padding:50px;'>ارفع مشروعك لبدء المعاينة</div>")
191
- with gr.TabItem("📝 محرر الكود"):
192
- editor = gr.Code(label="محتوى الملف", lines=25, interactive=True)
193
- save_manual = gr.Button("💾 حفظ التعديل اليدوي", size="sm")
194
-
195
- # --- ربط الأحداث ---
196
 
197
- def on_upload(files):
198
- log, choices = tank.sync_files(files)
199
- return log, gr.update(choices=choices), tank.build_preview()
200
 
201
- upload.upload(on_upload, upload, [status, file_list, preview])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
- send.click(chat_and_code, [msg, chat], chat).then(lambda: "", None, msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
- def on_apply(h):
206
- log, pre, choices = apply_and_preview(h)
207
- return log, pre, gr.update(choices=choices)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
- apply.click(on_apply, chat, [status, preview, file_list])
 
210
 
211
- file_list.change(lambda n: (tank.files_cache.get(n, ""), n), file_list, [editor, active_file])
 
212
 
213
- save_manual.click(lambda n, c: tank.update_file_content(n, c) or "✅ تم الحفظ يدوياً", [active_file, editor], status).then(tank.build_preview, None, preview)
 
214
 
215
- export_btn.click(tank.export_project, None, export_btn)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
  if __name__ == "__main__":
218
- demo.queue().launch()
 
 
 
6
  from pathlib import Path
7
  import gradio as gr
8
  from huggingface_hub import InferenceClient
9
+ from flask import Flask, send_from_directory, render_template_string, request
10
+ from watchdog.observers import Observer
11
+ from watchdog.events import FileSystemEventHandler
12
+ import tornado.ioloop
13
+ import tornado.web
14
+ import tornado.websocket
15
 
16
  # --------------------------- الإعدادات ---------------------------
17
  ALLOWED_EXTENSIONS = {
 
46
  shutil.copy(f.name, dest)
47
 
48
  self._reload_memory()
49
+ self._start_live_preview()
50
  return f"🚀 تم تحميل {len(self.files_cache)} ملف بنجاح.", list(self.files_cache.keys())
51
 
52
  def _reload_memory(self):
 
107
  out_path = tempfile.mktemp()
108
  shutil.make_archive(out_path, 'zip', self.work_dir)
109
  return out_path + ".zip"
110
+
111
+ def _start_live_preview(self):
112
+ class PreviewHandler(tornado.web.RequestHandler):
113
+ def get(self):
114
+ entry = next((f for f in ["index.html", "main.html"] if f in tank.files_cache),
115
+ next((f for f in tank.files_cache if f.endswith('.html')), None))
116
+ if not entry:
117
+ self.write("<div style='text-align:center; padding:50px;'>⚠️ لم يتم العثور على ملف HTML للمعاينة</div>")
118
+ return
119
+ self.write(tank.files_cache[entry])
120
+
121
+ class WebSocketHandler(tornado.websocket.WebSocketHandler):
122
+ clients = set()
123
+
124
+ def open(self):
125
+ WebSocketHandler.clients.add(self)
126
+
127
+ def on_close(self):
128
+ WebSocketHandler.clients.remove(self)
129
+
130
+ @classmethod
131
+ def send_update(cls, message):
132
+ for client in cls.clients:
133
+ client.write_message(message)
134
+
135
+ class Watcher(FileSystemEventHandler):
136
+ def on_modified(self, event):
137
+ if event.is_directory: return
138
+ if Path(event.src_path).suffix in ALLOWED_EXTENSIONS:
139
+ tank._reload_memory()
140
+ message = tank.build_preview()
141
+ WebSocketHandler.send_update(message)
142
+
143
+ self.watcher = Observer()
144
+ self.watcher.schedule(Watcher(), self.work_dir, recursive=True)
145
+ self.watcher.start()
146
+
147
+ self.server = tornado.web.Application([
148
+ (r"/preview", PreviewHandler),
149
+ (r"/ws", WebSocketHandler),
150
+ ])
151
+ self.server.listen(8888)
152
+ tornado.ioloop.IOLoop.current().start()
153
+
154
 
155
  tank = ProjectTank()
156
  client = InferenceClient(api_key=HF_TOKEN) if HF_TOKEN else None
 
215
 
216
  # --------------------------- الواجهة ---------------------------
217
 
218
+ app = Flask(__name__)
 
 
 
219
 
220
+ @app.route('/upload', methods=['POST'])
221
+ def upload_file():
222
+ files = request.files.getlist('files[]')
223
+ log, choices = tank.sync_files(files)
224
+ return log
225
+
226
+ @app.route('/files/<path:filename>')
227
+ def get_file(filename):
228
+ path = os.path.join(tank.work_dir, filename)
229
+ return send_from_directory(tank.work_dir, filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
+ @app.route('/project')
232
+ def get_project_structure():
233
+ return {"structure": tank.structure}
234
 
235
+ @app.route('/preview')
236
+ def serve_preview():
237
+ entry = next((f for f in ["index.html", "main.html"] if f in tank.files_cache),
238
+ next((f for f in tank.files_cache if f.endswith('.html')), None))
239
+ if not entry:
240
+ return "⚠️ لم يتم العثور على ملف HTML للمعاينة"
241
+ return render_template_string(tank.files_cache[entry])
242
+
243
+ @app.route('/chat', methods=['POST'])
244
+ def chat_endpoint():
245
+ message = request.form['message']
246
+ history = request.form.get('history', [])
247
+ if not client:
248
+ history.append({"role": "assistant", "content": "⚠️ مفقود HF_TOKEN"})
249
+ return str(history)
250
 
251
+ context = f"### PROJECT STRUCTURE ###\n{tank.structure}\n\n"
252
+ context += "### FULL FILE CONTENTS ###\n"
253
+ for name, content in tank.files_cache.items():
254
+ context += f"--- FILE: {name} ---\n{content}\n"
255
+
256
+ system_instr = (
257
+ "You are an expert AI IDE. Your goal is to help the user build and debug their project.\n"
258
+ "1. Analyze the project structure to understand how files interact.\n"
259
+ "2. Provide clear explanations.\n"
260
+ "3. When modifying code, use the format: [TARGET: filename] followed by a code block.\n"
261
+ "4. Always output the FULL content of the file in the code block."
262
+ )
263
+
264
+ messages = [{"role": "system", "content": system_instr}]
265
+ for m in history:
266
+ messages.append({"role": m["role"], "content": str(m)})
267
 
268
+ messages.append({"role": "user", "content": f"{context}\n\nTask: {message}"})
269
+
270
+ response = ""
271
+ history.append({"role": "user", "content": message})
272
+ history.append({"role": "assistant", "content": ""})
273
+
274
+ try:
275
+ stream = client.chat_completion(model=MODEL_ID, messages=messages, max_tokens=6000, stream=True)
276
+ for chunk in stream:
277
+ if hasattr(chunk, 'choices') and chunk.choices and len(chunk.choices) > 0:
278
+ token = chunk.choices[0].delta.content
279
+ if token:
280
+ response += token
281
+ history[-1]["content"] = response
282
+ return str(history)
283
+ except Exception as e:
284
+ history[-1]["content"] = f"❌ خطأ في السيرفر: {str(e)}"
285
+ return str(history)
286
+
287
+ @app.route('/apply', methods=['POST'])
288
+ def apply_endpoint():
289
+ history = request.form.get('history', [])
290
+ if not history: return "لا توجد بيانات"
291
 
292
+ content = history[-1]["content"]
293
+ matches = re.findall(r"\[TARGET:\s*(.*?)\]\s*```.*?\n(.*?)```", content, re.DOTALL | re.IGNORECASE)
294
 
295
+ if not matches:
296
+ return "⚠️ لم يتم اكتشاف ملفات للتحديث."
297
 
298
+ for filename, code in matches:
299
+ tank.update_file_content(filename.strip(), code.strip())
300
 
301
+ return "✅ تم تحديث الملفات وحقن المعاينة بنجاح!"
302
+
303
+ @app.route('/export')
304
+ def export_project():
305
+ return send_from_directory(Path(tank.export_project()).parent, Path(tank.export_project()).name)
306
+
307
+ @app.route('/')
308
+ def serve_index():
309
+ return """
310
+ <div>
311
+ <h1>AI Tank IDE Pro</h1>
312
+ <a href="/preview">معاينة المشروع</a><br/>
313
+ <input type="file" id="files" name="files[]" multiple />
314
+ <button onclick="uploadFiles()">ارفع المشروع</button>
315
+ <div id="status">جاهز للعمل</div>
316
+ <div id="preview" style="margin-top: 20px;"></div>
317
+ </div>
318
+ <script>
319
+ function uploadFiles() {
320
+ const formData = new FormData();
321
+ const fileInput = document.getElementById('files');
322
+ Array.from(fileInput.files).forEach(file => {
323
+ formData.append('files[]', file);
324
+ });
325
+
326
+ fetch('/upload', { method: 'POST', body: formData })
327
+ .then(response => response.text())
328
+ .then(log => {
329
+ document.getElementById('status').innerText = log;
330
+ if (log.includes('تم تحميل')) {
331
+ fetch('/preview')
332
+ .then(res => res.text())
333
+ .then(html => {
334
+ document.getElementById('preview').innerHTML = html;
335
+ });
336
+ }
337
+ });
338
+ }
339
+ </script>
340
+ """
341
 
342
  if __name__ == "__main__":
343
+ tank = ProjectTank()
344
+ client = InferenceClient(api_key=HF_TOKEN) if HF_TOKEN else None
345
+ app.run(host='0.0.0.0', port=5000)