AIencoder commited on
Commit
415ffbf
·
verified ·
1 Parent(s): 878577a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -110
app.py CHANGED
@@ -1,39 +1,35 @@
1
  import gradio as gr
2
  import torch
3
  import time
4
- import re
 
 
5
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
6
- from typing import Dict, List, Tuple, Optional
7
  import os
8
- import json
9
  from functools import lru_cache
10
 
11
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
12
 
13
- # CPU-optimized configuration
14
  MODEL_NAME = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
15
  DEVICE = "cpu"
16
  DTYPE = torch.float32
17
 
18
- # --- File System Class ---
19
  class FileSystem:
20
  def __init__(self):
21
  self.files = {
22
- "main.js": "// Start coding here\nconsole.log('Hello Axon Pro');",
23
- "utils.js": "// Utility functions\nfunction add(a, b) {\n return a + b;\n}",
24
- "style.css": "/* Add your styles here */\nbody {\n font-family: monospace;\n}"
25
  }
26
- self.current_file = "main.js"
27
- self.history = []
28
- self.max_history = 50
29
 
30
  def save_file(self, content: str) -> None:
31
  if self.current_file:
32
  self.files[self.current_file] = content
33
 
34
- def get_file(self, filename: str) -> str:
35
- return self.files.get(filename, "")
36
-
37
  def get_current_file_content(self) -> str:
38
  return self.files.get(self.current_file, "")
39
 
@@ -48,19 +44,13 @@ class FileSystem:
48
 
49
  def get_all_files(self) -> List[str]:
50
  return list(self.files.keys())
51
-
52
- def get_context(self) -> str:
53
- context = ""
54
- for filename, content in self.files.items():
55
- context += f"// File: {filename}\n{content}\n\n"
56
- return context
57
 
58
  fs = FileSystem()
59
 
60
  # --- Model Loading ---
61
  @lru_cache(maxsize=1)
62
  def load_model():
63
- print("Loading TinyLlama model for CPU... This may take a moment.")
64
  start_time = time.time()
65
  try:
66
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
@@ -79,37 +69,49 @@ def load_model():
79
  top_p=0.95,
80
  do_sample=True
81
  )
82
- print(f"Model loaded successfully in {time.time() - start_time:.2f} seconds!")
83
  return pipe
84
  except Exception as e:
85
- print(f"Error loading model: {str(e)}")
86
  return None
87
 
88
  # --- Logic Functions ---
 
89
  def run_code(code: str) -> Tuple[str, str]:
 
90
  start_time = time.time()
 
 
91
  try:
92
- # Placeholder for actual execution logic
93
- output = f"✓ Code executed successfully\nExecution time: {time.time() - start_time:.4f}s"
94
- return output, ""
 
 
 
 
 
 
 
 
95
  except Exception as e:
96
- return "", f" Error: {str(e)}"
97
 
98
  def generate_completion(code: str) -> str:
99
  model = load_model()
100
- if not model: return "Error: Model failed to load."
101
 
102
- prompt = f"<|system|>You are a JS expert. Complete the code. No explanations.</s><|user|>\n{code}\n</s><|assistant|>"
103
  try:
104
  result = model(prompt, num_return_sequences=1, return_full_text=False)
105
  return result[0]['generated_text'].strip()
106
  except Exception as e:
107
- return f"/* Error: {str(e)} */"
108
 
109
  def explain_code(code: str) -> str:
110
  model = load_model()
111
  if not model: return "Error loading model."
112
- prompt = f"<|system|>Explain this code concisely.</s><|user|>\n{code}\n</s><|assistant|>"
113
  try:
114
  result = model(prompt, max_new_tokens=512)
115
  return result[0]['generated_text'].strip()
@@ -119,7 +121,7 @@ def explain_code(code: str) -> str:
119
  def refactor_code(code: str) -> str:
120
  model = load_model()
121
  if not model: return "Error loading model."
122
- prompt = f"<|system|>Refactor this JS code for best practices.</s><|user|>\n{code}\n</s><|assistant|>"
123
  try:
124
  result = model(prompt, max_new_tokens=512)
125
  return result[0]['generated_text'].strip()
@@ -129,7 +131,7 @@ def refactor_code(code: str) -> str:
129
  def generate_code(prompt_text: str) -> str:
130
  model = load_model()
131
  if not model: return "Error loading model."
132
- prompt = f"<|system|>Write JavaScript code for: {prompt_text}</s><|assistant|>"
133
  try:
134
  result = model(prompt, max_new_tokens=512)
135
  return result[0]['generated_text'].strip()
@@ -137,20 +139,17 @@ def generate_code(prompt_text: str) -> str:
137
  return str(e)
138
 
139
  def create_diff_view(original: str, modified: str) -> str:
140
- lines_original = original.split('\n')
141
- lines_modified = modified.split('\n')
142
- diff = []
143
  import difflib
144
- for line in difflib.unified_diff(lines_original, lines_modified, lineterm=""):
145
- diff.append(line)
146
  return "\n".join(diff)
147
 
148
- # --- Gradio UI Configuration ---
149
 
150
- # 1. THEME: Cleaned up to avoid errors
151
  theme = gr.themes.Default(
152
  font=[gr.themes.GoogleFont('JetBrains Mono'), 'monospace'],
153
- font_mono=[gr.themes.GoogleFont('JetBrains Mono'), 'monospace'],
154
  primary_hue="blue",
155
  neutral_hue="gray",
156
  ).set(
@@ -160,36 +159,25 @@ theme = gr.themes.Default(
160
  body_text_color="*neutral_50",
161
  )
162
 
163
- # 2. CSS: Styling
164
  css = """
165
- /* Force square corners globally */
166
  * { border-radius: 0 !important; }
167
-
168
  .editor-container { height: 50vh; }
169
- .terminal { height: 15vh; background-color: #1e1e1e; color: #d4d4d4; overflow: auto; }
170
- .diff-view { height: 40vh; overflow: auto; }
171
- .file-explorer { height: 50vh; background-color: #252526; overflow: auto; }
172
- .ai-chat { height: 40vh; overflow: auto; }
173
- .status-bar { background-color: #007acc; color: white; padding: 5px; text-align: center; }
174
- .btn { min-width: 100px; }
175
- .monaco-editor { height: 100% !important; }
176
- .theme-dark { --body-bg: #1e1e1e; --editor-bg: #1e1e1e; }
177
- .button-large { min-height: 40px !important; }
178
- .tab-nav { background-color: #252526 !important; }
179
- .tabitem { background-color: #1e1e1e !important; }
180
- .code-wrap { white-space: pre-wrap !important; word-break: break-word !important; }
181
  """
182
 
183
- with gr.Blocks(title="Axon Pro - Free AI IDE", theme=theme, css=css) as demo:
184
 
185
- gr.Markdown("# Axon Pro — Free AI-Powered Code IDE")
186
 
187
  with gr.Tabs() as main_tabs:
188
  with gr.Tab("Editor", id="editor-tab"):
189
  with gr.Row(equal_height=True):
190
- # File Explorer
191
  with gr.Column(scale=1, min_width=200):
192
- gr.Markdown("### 📁 File Explorer")
193
  file_list = gr.Dropdown(
194
  choices=fs.get_all_files(),
195
  value=fs.current_file,
@@ -200,14 +188,15 @@ with gr.Blocks(title="Axon Pro - Free AI IDE", theme=theme, css=css) as demo:
200
  with gr.Row():
201
  new_file_btn = gr.Button("➕ New", variant="secondary")
202
  save_btn = gr.Button("💾 Save", variant="secondary")
203
- new_file_name = gr.Textbox(placeholder="filename.js", label="New File Name", container=False)
204
 
205
- # Main Editor Area
206
  with gr.Column(scale=4):
 
207
  editor = gr.Code(
208
  value=fs.get_current_file_content(),
209
  label="Code Editor",
210
- language="javascript",
211
  lines=20,
212
  interactive=True,
213
  elem_classes="code-wrap"
@@ -222,36 +211,37 @@ with gr.Blocks(title="Axon Pro - Free AI IDE", theme=theme, css=css) as demo:
222
  with gr.Tabs():
223
  with gr.Tab("Terminal", id="terminal-tab"):
224
  terminal = gr.Textbox(
225
- value="$ _",
226
- lines=5,
227
  interactive=False,
228
- elem_classes="terminal"
 
229
  )
230
- clear_btn = gr.Button("CLEAR TERMINAL", variant="secondary", size="sm")
231
 
232
  with gr.Tab("AI Chat", id="chat-tab"):
233
  chat_history = gr.Chatbot(label="Axon AI", height=300)
234
  with gr.Row():
235
- chat_input = gr.Textbox(placeholder="Ask to generate code...", scale=7, container=False)
236
  send_btn = gr.Button("Generate", variant="primary", scale=1)
237
 
238
  with gr.Tab("Diff View", id="diff-tab"):
239
- # FIX: Changed language="diff" to language="javascript" to prevent crash
240
- diff_view = gr.Code(label="AI Changes", language="javascript", elem_classes="diff-view code-wrap", interactive=False)
241
  with gr.Row():
242
  apply_btn = gr.Button("Apply Changes", variant="primary")
243
  discard_btn = gr.Button("Discard Changes", variant="secondary")
244
 
245
- status_bar = gr.Markdown(f"**AXON PRO v1.0** | CPU Mode | TinyLlama-1.1B", elem_classes="status-bar")
246
 
247
- # State management
248
  current_file_state = gr.State(fs.current_file)
249
  diff_original_state = gr.State("")
250
  diff_modified_state = gr.State("")
251
  diff_mode_state = gr.State(False)
252
 
253
- # Event Handlers
254
- def update_file_content(content):
255
  fs.save_file(content)
256
  return fs.get_current_file_content()
257
 
@@ -259,84 +249,74 @@ with gr.Blocks(title="Axon Pro - Free AI IDE", theme=theme, css=css) as demo:
259
  fs.set_current_file(filename)
260
  return fs.get_current_file_content(), filename
261
 
262
- def create_new_file(name):
263
  if name and "." in name:
264
  fs.create_file(name)
265
  return gr.update(choices=fs.get_all_files(), value=name), fs.get_current_file_content()
266
  return gr.update(choices=fs.get_all_files()), fs.get_current_file_content()
267
 
268
- def run_code_wrapper(content):
269
  out, err = run_code(content)
270
- return out if not err else err
 
 
271
 
272
- def complete_code_wrapper(content):
273
  comp = generate_completion(content)
274
- return content + comp
275
 
276
- def explain_code_wrapper(content):
277
  explanation = explain_code(content)
278
  fs.save_file(content)
279
  diff_orig = content
280
- diff_mod = f"/* EXPLANATION:\n{explanation}\n*/\n\n{content}"
281
  diff = create_diff_view(diff_orig, diff_mod)
282
  return diff, diff_orig, diff_mod, True
283
 
284
- def refactor_code_wrapper(content):
285
  refactored = refactor_code(content)
286
  fs.save_file(content)
287
  diff = create_diff_view(content, refactored)
288
  return diff, content, refactored, True
289
 
290
- def generate_code_wrapper(prompt, history):
291
  generated = generate_code(prompt)
292
  diff = create_diff_view("", generated)
293
  new_hist = history + [[prompt, "Generated code available in Diff View"]]
294
  return diff, "", generated, True, new_hist, ""
295
 
296
- # Binding Events
297
- editor.change(update_file_content, editor, None)
298
  file_list.change(load_file, file_list, [editor, current_file_state])
299
- new_file_btn.click(create_new_file, new_file_name, [file_list, editor])
300
 
301
- run_btn.click(run_code_wrapper, editor, terminal)
302
- complete_btn.click(complete_code_wrapper, editor, editor)
303
- clear_btn.click(lambda: "$ _", None, terminal)
304
 
305
- # Tab switching actions
306
  explain_btn.click(
307
- explain_code_wrapper, editor,
308
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
309
- ).then(
310
- lambda: gr.Tabs(selected="diff-tab"), None, main_tabs
311
- )
312
 
313
  refactor_btn.click(
314
- refactor_code_wrapper, editor,
315
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
316
- ).then(
317
- lambda: gr.Tabs(selected="diff-tab"), None, main_tabs
318
- )
319
 
320
  chat_input.submit(
321
- generate_code_wrapper, [chat_input, chat_history],
322
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state, chat_history, chat_input]
323
- ).then(
324
- lambda: gr.Tabs(selected="diff-tab"), None, main_tabs
325
- )
326
 
327
  apply_btn.click(
328
  lambda mod: (mod, False), diff_modified_state, [editor, diff_mode_state]
329
- ).then(
330
- lambda: gr.Tabs(selected="editor-tab"), None, main_tabs
331
- )
332
 
333
  discard_btn.click(
334
  lambda: (gr.update(), False), None, [editor, diff_mode_state]
335
- ).then(
336
- lambda: gr.Tabs(selected="editor-tab"), None, main_tabs
337
- )
338
 
339
  if __name__ == "__main__":
340
- # If the warning about theme/css being in launch() persists, you can move them here.
341
- # But for now, we keep them in Blocks to support older/standard versions as well.
342
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
  import torch
3
  import time
4
+ import sys
5
+ from io import StringIO
6
+ import contextlib
7
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
8
+ from typing import List, Tuple
9
  import os
 
10
  from functools import lru_cache
11
 
12
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
13
 
14
+ # --- Configuration ---
15
  MODEL_NAME = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
16
  DEVICE = "cpu"
17
  DTYPE = torch.float32
18
 
19
+ # --- File System (Python Version) ---
20
  class FileSystem:
21
  def __init__(self):
22
  self.files = {
23
+ "main.py": "# Start coding here\nprint('Hello Axon Pro (Python Edition)')",
24
+ "utils.py": "# Utility functions\ndef add(a, b):\n return a + b",
25
+ "notes.txt": "Project requirements:\n- Build a cool AI app\n- Use Gradio"
26
  }
27
+ self.current_file = "main.py"
 
 
28
 
29
  def save_file(self, content: str) -> None:
30
  if self.current_file:
31
  self.files[self.current_file] = content
32
 
 
 
 
33
  def get_current_file_content(self) -> str:
34
  return self.files.get(self.current_file, "")
35
 
 
44
 
45
  def get_all_files(self) -> List[str]:
46
  return list(self.files.keys())
 
 
 
 
 
 
47
 
48
  fs = FileSystem()
49
 
50
  # --- Model Loading ---
51
  @lru_cache(maxsize=1)
52
  def load_model():
53
+ print("Loading TinyLlama model for CPU...")
54
  start_time = time.time()
55
  try:
56
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
 
69
  top_p=0.95,
70
  do_sample=True
71
  )
72
+ print(f"Model loaded in {time.time() - start_time:.2f}s")
73
  return pipe
74
  except Exception as e:
75
+ print(f"Error loading model: {e}")
76
  return None
77
 
78
  # --- Logic Functions ---
79
+
80
  def run_code(code: str) -> Tuple[str, str]:
81
+ """Actually executes Python code and captures stdout."""
82
  start_time = time.time()
83
+ output_buffer = StringIO()
84
+
85
  try:
86
+ # Capture standard output
87
+ with contextlib.redirect_stdout(output_buffer):
88
+ # dicts for globals/locals to keep environment somewhat contained
89
+ exec(code, {}, {})
90
+
91
+ exec_time = time.time() - start_time
92
+ result = output_buffer.getvalue()
93
+ if not result:
94
+ result = "(No output)"
95
+
96
+ return f"{result}\n\n[Execution time: {exec_time:.4f}s]", ""
97
  except Exception as e:
98
+ return output_buffer.getvalue(), f"Traceback (most recent call last):\n{str(e)}"
99
 
100
  def generate_completion(code: str) -> str:
101
  model = load_model()
102
+ if not model: return "# Error: Model failed to load."
103
 
104
+ prompt = f"<|system|>You are a Python expert. Complete the code. No explanations.</s><|user|>\n{code}\n</s><|assistant|>"
105
  try:
106
  result = model(prompt, num_return_sequences=1, return_full_text=False)
107
  return result[0]['generated_text'].strip()
108
  except Exception as e:
109
+ return f"# Error: {str(e)}"
110
 
111
  def explain_code(code: str) -> str:
112
  model = load_model()
113
  if not model: return "Error loading model."
114
+ prompt = f"<|system|>Explain this Python code concisely.</s><|user|>\n{code}\n</s><|assistant|>"
115
  try:
116
  result = model(prompt, max_new_tokens=512)
117
  return result[0]['generated_text'].strip()
 
121
  def refactor_code(code: str) -> str:
122
  model = load_model()
123
  if not model: return "Error loading model."
124
+ prompt = f"<|system|>Refactor this Python code for PEP 8 and best practices.</s><|user|>\n{code}\n</s><|assistant|>"
125
  try:
126
  result = model(prompt, max_new_tokens=512)
127
  return result[0]['generated_text'].strip()
 
131
  def generate_code(prompt_text: str) -> str:
132
  model = load_model()
133
  if not model: return "Error loading model."
134
+ prompt = f"<|system|>Write Python code for: {prompt_text}</s><|assistant|>"
135
  try:
136
  result = model(prompt, max_new_tokens=512)
137
  return result[0]['generated_text'].strip()
 
139
  return str(e)
140
 
141
  def create_diff_view(original: str, modified: str) -> str:
142
+ lines_original = original.splitlines()
143
+ lines_modified = modified.splitlines()
 
144
  import difflib
145
+ diff = difflib.unified_diff(lines_original, lines_modified, lineterm="")
 
146
  return "\n".join(diff)
147
 
148
+ # --- Gradio UI ---
149
 
150
+ # Theme & CSS
151
  theme = gr.themes.Default(
152
  font=[gr.themes.GoogleFont('JetBrains Mono'), 'monospace'],
 
153
  primary_hue="blue",
154
  neutral_hue="gray",
155
  ).set(
 
159
  body_text_color="*neutral_50",
160
  )
161
 
 
162
  css = """
163
+ /* Force square corners */
164
  * { border-radius: 0 !important; }
 
165
  .editor-container { height: 50vh; }
166
+ .terminal { height: 15vh; background-color: #1e1e1e; color: #d4d4d4; font-family: 'JetBrains Mono', monospace; }
167
+ .diff-view { height: 40vh; }
168
+ .code-wrap { white-space: pre-wrap !important; }
 
 
 
 
 
 
 
 
 
169
  """
170
 
171
+ with gr.Blocks(title="Axon Pro - Python IDE", theme=theme, css=css) as demo:
172
 
173
+ gr.Markdown("# 🐍 Axon Pro — Python AI IDE")
174
 
175
  with gr.Tabs() as main_tabs:
176
  with gr.Tab("Editor", id="editor-tab"):
177
  with gr.Row(equal_height=True):
178
+ # Sidebar
179
  with gr.Column(scale=1, min_width=200):
180
+ gr.Markdown("### 📁 Explorer")
181
  file_list = gr.Dropdown(
182
  choices=fs.get_all_files(),
183
  value=fs.current_file,
 
188
  with gr.Row():
189
  new_file_btn = gr.Button("➕ New", variant="secondary")
190
  save_btn = gr.Button("💾 Save", variant="secondary")
191
+ new_file_name = gr.Textbox(placeholder="script.py", label="New File", container=False)
192
 
193
+ # Editor
194
  with gr.Column(scale=4):
195
+ # Changed language to 'python'
196
  editor = gr.Code(
197
  value=fs.get_current_file_content(),
198
  label="Code Editor",
199
+ language="python",
200
  lines=20,
201
  interactive=True,
202
  elem_classes="code-wrap"
 
211
  with gr.Tabs():
212
  with gr.Tab("Terminal", id="terminal-tab"):
213
  terminal = gr.Textbox(
214
+ value=">>> Ready to run Python code...",
215
+ lines=6,
216
  interactive=False,
217
+ elem_classes="terminal",
218
+ label="Output"
219
  )
220
+ clear_btn = gr.Button("Clear Output", variant="secondary", size="sm")
221
 
222
  with gr.Tab("AI Chat", id="chat-tab"):
223
  chat_history = gr.Chatbot(label="Axon AI", height=300)
224
  with gr.Row():
225
+ chat_input = gr.Textbox(placeholder="Ask to generate Python code...", scale=7, container=False)
226
  send_btn = gr.Button("Generate", variant="primary", scale=1)
227
 
228
  with gr.Tab("Diff View", id="diff-tab"):
229
+ # Changed language to 'python' for diff view as well
230
+ diff_view = gr.Code(label="AI Changes", language="python", elem_classes="diff-view code-wrap", interactive=False)
231
  with gr.Row():
232
  apply_btn = gr.Button("Apply Changes", variant="primary")
233
  discard_btn = gr.Button("Discard Changes", variant="secondary")
234
 
235
+ status_bar = gr.Markdown(f"**AXON PRO v1.0** | Python 3.x | CPU Mode | TinyLlama-1.1B", elem_classes="status-bar")
236
 
237
+ # State
238
  current_file_state = gr.State(fs.current_file)
239
  diff_original_state = gr.State("")
240
  diff_modified_state = gr.State("")
241
  diff_mode_state = gr.State(False)
242
 
243
+ # Functions
244
+ def update_file(content):
245
  fs.save_file(content)
246
  return fs.get_current_file_content()
247
 
 
249
  fs.set_current_file(filename)
250
  return fs.get_current_file_content(), filename
251
 
252
+ def create_file(name):
253
  if name and "." in name:
254
  fs.create_file(name)
255
  return gr.update(choices=fs.get_all_files(), value=name), fs.get_current_file_content()
256
  return gr.update(choices=fs.get_all_files()), fs.get_current_file_content()
257
 
258
+ def run_wrapper(content):
259
  out, err = run_code(content)
260
+ if err:
261
+ return f"{out}\n\n=== ERROR ===\n{err}"
262
+ return out
263
 
264
+ def complete_wrapper(content):
265
  comp = generate_completion(content)
266
+ return content + "\n" + comp
267
 
268
+ def explain_wrapper(content):
269
  explanation = explain_code(content)
270
  fs.save_file(content)
271
  diff_orig = content
272
+ diff_mod = f'"""\nEXPLANATION:\n{explanation}\n"""\n\n{content}'
273
  diff = create_diff_view(diff_orig, diff_mod)
274
  return diff, diff_orig, diff_mod, True
275
 
276
+ def refactor_wrapper(content):
277
  refactored = refactor_code(content)
278
  fs.save_file(content)
279
  diff = create_diff_view(content, refactored)
280
  return diff, content, refactored, True
281
 
282
+ def generate_wrapper(prompt, history):
283
  generated = generate_code(prompt)
284
  diff = create_diff_view("", generated)
285
  new_hist = history + [[prompt, "Generated code available in Diff View"]]
286
  return diff, "", generated, True, new_hist, ""
287
 
288
+ # Wiring
289
+ editor.change(update_file, editor, None)
290
  file_list.change(load_file, file_list, [editor, current_file_state])
291
+ new_file_btn.click(create_file, new_file_name, [file_list, editor])
292
 
293
+ run_btn.click(run_wrapper, editor, terminal)
294
+ complete_btn.click(complete_wrapper, editor, editor)
295
+ clear_btn.click(lambda: ">>> Ready...", None, terminal)
296
 
297
+ # Tabs logic
298
  explain_btn.click(
299
+ explain_wrapper, editor,
300
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
301
+ ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
 
 
302
 
303
  refactor_btn.click(
304
+ refactor_wrapper, editor,
305
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
306
+ ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
 
 
307
 
308
  chat_input.submit(
309
+ generate_wrapper, [chat_input, chat_history],
310
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state, chat_history, chat_input]
311
+ ).then(lambda: gr.Tabs(selected="diff-tab"), None, main_tabs)
 
 
312
 
313
  apply_btn.click(
314
  lambda mod: (mod, False), diff_modified_state, [editor, diff_mode_state]
315
+ ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)
 
 
316
 
317
  discard_btn.click(
318
  lambda: (gr.update(), False), None, [editor, diff_mode_state]
319
+ ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)
 
 
320
 
321
  if __name__ == "__main__":
 
 
322
  demo.launch(server_name="0.0.0.0", server_port=7860)