1- """patternanalyzer.tui — Basit Textual TUI uygulaması ."""
2-
1+ """patternanalyzer.tui — Simple Textual TUI application ."""
2+
33from typing import Optional
44from textual .app import App , ComposeResult
55# ScrollView API moved between textual versions; try importing from widgets first,
@@ -27,15 +27,15 @@ class ScrollView(Container): # type: ignore
2727import json
2828
2929class PatternAnalyzerTUI (App ):
30- """Textual.app tabanlı basit uygulama .
31-
32- Bu arayüz sadece istenen widget'ları sağlar :
33- - Dosya seçimi için DirectoryTree
34- - Çalıştırılacak testleri seçmek için Checkbox'lar (engine.get_available_tests())
35- - Başlat ve Çıkış için Button'lar
36- - Analiz sonuçları için DataTable ve tıklanabilir sonuç listesi (modal ile metrik gösterimi )
30+ """Simple application based on Textual.app .
31+
32+ This interface only provides the required widgets :
33+ - DirectoryTree for file selection
34+ - Checkboxes for selecting tests to run (engine.get_available_tests())
35+ - Start and Exit buttons
36+ - DataTable for analysis results and clickable results list (modal for metric display )
3737 """
38-
38+
3939 CSS = """
4040 #body { height: 1fr; }
4141 #left { width: 50%; min-width: 30; }
@@ -47,59 +47,59 @@ class PatternAnalyzerTUI(App):
4747 #results_table { height: 1fr; border: round $accent; padding: 1; }
4848 #results_scroll { height: 10; border: round $accent; padding: 1; }
4949 """
50-
50+
5151 def compose (self ) -> ComposeResult :
52- """Bileşenleri oluştur ."""
52+ """Compose the components ."""
5353 yield Header ()
5454 with Container (id = "body" ):
5555 with Horizontal ():
56- # Sol: dosya ağacı / seçici
56+ # Left: file tree / selector
5757 yield Vertical (
58- Static ("Dosya seçimi " , id = "file_label" ),
58+ Static ("File Selection " , id = "file_label" ),
5959 DirectoryTree ("." , id = "file_tree" ),
6060 id = "left" ,
6161 )
62- # Sağ : test listesi, sonuçlar ve kontrol düğmeleri
62+ # Right : test list, results, and control buttons
6363 yield Vertical (
64- Static ("Mevcut Testler (Checkbox ile seçin )" , id = "tests_label" ),
64+ Static ("Available Tests (Select with Checkboxes )" , id = "tests_label" ),
6565 ScrollView (id = "tests_scroll" ),
6666 Static ("Scorecard" , id = "score_label" ),
6767 Static ("" , id = "scorecard" , expand = False ),
68- Static ("Sonuçlar (tablo )" , id = "results_label" ),
68+ Static ("Results (Table )" , id = "results_label" ),
6969 DataTable (id = "results_table" ),
70- Static ("Tıklanabilir sonuç listesi " , id = "results_list_label" ),
70+ Static ("Clickable Results List " , id = "results_list_label" ),
7171 ScrollView (id = "results_scroll" ),
72- Static ("" , id = "status" , expand = False ), # Status mesajları için
72+ Static ("" , id = "status" , expand = False ), # For status messages
7373 Horizontal (
74- Button ("Başlat " , id = "start_btn" , variant = "success" ),
75- Button ("Çıkış " , id = "exit_btn" , variant = "error" ),
74+ Button ("Start " , id = "start_btn" , variant = "success" ),
75+ Button ("Exit " , id = "exit_btn" , variant = "error" ),
7676 id = "controls" ,
7777 ),
7878 id = "right" ,
7979 )
8080 yield Footer ()
81-
81+
8282 def on_mount (self ) -> None :
83- """Uygulama mount edildikten sonra test checkbox'larını yükle ."""
84- # Başlangıçta analiz çalışmıyor
83+ """Load test checkboxes after the app is mounted ."""
84+ # Analysis not running initially
8585 self ._analysis_running = False
8686 # Mapping for clickable result buttons -> metrics
8787 self ._result_metrics = {}
88-
89- # Motor örneği sadece test isimlerini almak için kullanılır; çalıştırma yapılmayacak .
88+
89+ # Engine instance used only to get test names; no execution will be performed .
9090 try :
9191 eng = Engine ()
9292 tests = eng .get_available_tests ()
9393 except Exception :
9494 tests = []
95-
95+
9696 tests_scroll : Optional [ScrollView ] = self .query_one ("#tests_scroll" , ScrollView )
97- # Dynamic olarak Checkbox'ları ekle
97+ # Dynamically add Checkboxes
9898 for i , tname in enumerate (tests ):
9999 cb = Checkbox (tname , id = f"test_{ i } " )
100100 tests_scroll .mount (cb )
101-
102- # Prepare DataTable columns (keşif amaçlı, tekrar oluşturmayı önlemek için )
101+
102+ # Prepare DataTable columns (for discovery, to avoid recreation )
103103 try :
104104 table = self .query_one ("#results_table" , DataTable )
105105 # clear any existing columns/rows
@@ -116,9 +116,9 @@ def on_mount(self) -> None:
116116 pass
117117 except Exception :
118118 pass
119-
119+
120120 def _format_scorecard (self , scorecard : dict ) -> str :
121- """Scorecard'ı biçimlendirilmiş metin olarak döndür ."""
121+ """Format the scorecard as formatted text ."""
122122 try :
123123 lines = []
124124 lines .append (f"Failed tests: { scorecard .get ('failed_tests' )} " )
@@ -134,23 +134,23 @@ def _format_scorecard(self, scorecard: dict) -> str:
134134 return "\n " .join (lines )
135135 except Exception :
136136 return json .dumps (scorecard , indent = 2 , ensure_ascii = False )
137-
137+
138138 def _display_results (self , output : dict ) -> None :
139- """Engine.analyze çıktısını UI içinde göster : scorecard ve sonuç listesi ."""
139+ """Display Engine.analyze output in the UI : scorecard and results list ."""
140140 try :
141141 scorecard = output .get ("scorecard" , {}) if isinstance (output , dict ) else {}
142142 results = output .get ("results" , []) if isinstance (output , dict ) else []
143143 except Exception :
144144 scorecard = {}
145145 results = []
146-
146+
147147 # Update scorecard Static
148148 try :
149149 sc = self .query_one ("#scorecard" , Static )
150150 sc .update (self ._format_scorecard (scorecard ))
151151 except Exception :
152152 pass
153-
153+
154154 # Populate DataTable summary
155155 try :
156156 table = self .query_one ("#results_table" , DataTable )
@@ -196,7 +196,7 @@ def _display_results(self, output: dict) -> None:
196196 self ._result_metrics [f"res_btn_{ i } " ] = {"test_name" : tname , "metrics" : metrics }
197197 except Exception :
198198 pass
199-
199+
200200 # Populate clickable results list (Buttons inside ScrollView)
201201 try :
202202 rs = self .query_one ("#results_scroll" , ScrollView )
@@ -212,62 +212,62 @@ def _display_results(self, output: dict) -> None:
212212 rs .mount (btn )
213213 except Exception :
214214 pass
215-
215+
216216 def _show_loading (self ) -> None :
217- """LoadingIndicator'ı sağ panelde göster ."""
218- # Eğer zaten gösteriliyorsa yeniden mount etme
217+ """Show LoadingIndicator in the right panel ."""
218+ # If already shown, do not remount
219219 if self .query ("#loading" ):
220220 return
221221 try :
222222 right = self .query_one ("#right" , Vertical )
223223 right .mount (LoadingIndicator (id = "loading" ))
224224 except Exception :
225225 pass
226-
226+
227227 def _hide_loading (self ) -> None :
228- """LoadingIndicator'ı kaldır ."""
228+ """Remove the LoadingIndicator ."""
229229 try :
230230 loading = self .query_one ("#loading" )
231231 loading .remove ()
232232 except Exception :
233233 pass
234-
234+
235235 def on_button_pressed (self , event : Button .Pressed ) -> None :
236- """Butonlara basılınca çalışır: Başlat, Çıkış, sonuç butonları ve modal kapatma ."""
236+ """Triggered when buttons are pressed: Start, Exit, result buttons, and modal close ."""
237237 btn_id = getattr (event .button , "id" , None )
238238 if btn_id == "exit_btn" :
239239 self .exit ()
240240 return
241-
241+
242242 if btn_id == "start_btn" :
243- # Seçili testleri ve seçili dosya yolunu topla
243+ # Collect selected tests and selected file path
244244 selected_tests = [cb .label for cb in self .query (Checkbox ) if getattr (cb , "id" , "" ).startswith ("test_" ) and cb .value ]
245245 file_tree = self .query_one (DirectoryTree )
246- # DirectoryTree içindeki seçili dosya bilgisini güvenli okumaya çalış
246+ # Safely read selected file info from DirectoryTree
247247 file_path = None
248248 for attr in ("path" , "cursor_path" , "selected_path" , "cursor" , "selected_node" ):
249249 val = getattr (file_tree , attr , None )
250250 if val :
251- # selected_node olabilir; path özniteliği varsa kullan
251+ # selected_node may exist; use path attribute if available
252252 if hasattr (val , "path" ):
253253 file_path = getattr (val , "path" )
254254 else :
255255 file_path = str (val )
256256 break
257-
257+
258258 status = self .query_one ("#status" , Static )
259-
260- # Eğer analiz zaten çalışıyorsa yeni analiz başlatma
259+
260+ # If analysis is already running, do not start a new one
261261 if getattr (self , "_analysis_running" , False ):
262- status .update ("Analiz zaten çalışıyor " )
262+ status .update ("Analysis is already running " )
263263 return
264-
265- # Hazırlık: Loading göster, flag set
264+
265+ # Preparation: Show loading, set flag
266266 self ._analysis_running = True
267267 self ._show_loading ()
268- status .update (f"Analiz başlatıldı — seçili testler : { len (selected_tests )} " )
269-
270- # Worker fonksiyonu: Engine.analyze'ı arka planda çağırır
268+ status .update (f"Analysis started — selected tests : { len (selected_tests )} " )
269+
270+ # Worker function: Calls Engine.analyze in the background
271271 def _worker ():
272272 try :
273273 eng = Engine ()
@@ -279,49 +279,49 @@ def _worker():
279279 except Exception :
280280 data = b""
281281 config = {"tests" : [{"name" : t , "params" : {}} for t in selected_tests ]}
282- # Motorun ağır işi burada yapılır
282+ # Engine's heavy work is done here
283283 return eng .analyze (data , config )
284284 except Exception as e :
285285 return {"error" : str (e )}
286-
287- # tamamlandığında çağrılacak callback
286+
287+ # Callback to be called when done
288288 def _on_done (result ):
289289 self ._analysis_running = False
290290 self ._hide_loading ()
291291 try :
292292 if isinstance (result , dict ) and "error" in result :
293- status .update (f"Analiz hatası : { result ['error' ]} " )
293+ status .update (f"Analysis error : { result ['error' ]} " )
294294 else :
295- status .update ("Analiz tamamlandı — sonuçlar gösteriliyor " )
295+ status .update ("Analysis completed — results displayed " )
296296 self ._display_results (result or {})
297297 except Exception as e :
298- status .update (f"Görüntüleme hatası : { e } " )
299-
300- # Standart threading ile arka plan çalıştır
298+ status .update (f"Display error : { e } " )
299+
300+ # Run in background with standard threading
301301 try :
302302 import threading
303303 def _thread_worker ():
304304 result = _worker ()
305- # Callback'i main thread'de çalıştır
305+ # Run callback in main thread
306306 self .call_from_thread (_on_done , result )
307307 thread = threading .Thread (target = _thread_worker , daemon = True )
308308 thread .start ()
309309 except Exception as e :
310- # Hata durumunda temizle
310+ # Clean up in case of error
311311 self ._analysis_running = False
312312 self ._hide_loading ()
313- status .update (f"Analiz başlatılamadı : { e } " )
313+ status .update (f"Analysis could not start : { e } " )
314314 return
315-
316- # Modal kapatma düğmesi
315+
316+ # Modal close button
317317 if btn_id == "modal_close" :
318318 try :
319319 self .pop_screen ()
320320 except Exception :
321321 pass
322322 return
323-
324- # Sonuç listesi butonlarına tıklama: modal içinde metrikleri göster
323+
324+ # Clicking on result list buttons: show metrics in modal
325325 if isinstance (btn_id , str ) and btn_id .startswith ("res_btn_" ):
326326 info = self ._result_metrics .get (btn_id )
327327 if not info :
@@ -337,19 +337,19 @@ def _thread_worker():
337337 self .push_screen (MetricsModal (test_name , metrics_text ))
338338 except Exception :
339339 pass
340-
341- # Ek event handlerler veya yardımcılar gerektiğinde buraya eklenebilir .
342-
340+
341+ # Additional event handlers or helpers can be added here if needed .
342+
343343class MetricsModal (ModalScreen ):
344- """Basit modal ekran: seçili testin metriklerini gösterir ."""
344+ """Simple modal screen: displays metrics for the selected test ."""
345345 def __init__ (self , test_name : str , metrics_text : str ) -> None :
346346 super ().__init__ ()
347347 self .test_name = test_name
348348 self .metrics_text = metrics_text
349-
349+
350350 def compose (self ) -> ComposeResult :
351351 yield Vertical (
352- Static (f"Metrikler — { self .test_name } " , id = "modal_title" ),
352+ Static (f"Metrics — { self .test_name } " , id = "modal_title" ),
353353 (
354354 Static (self .metrics_text , id = "modal_metrics" )
355355 if _SCROLL_SRC == 'fallback'
@@ -360,9 +360,9 @@ def compose(self) -> ComposeResult:
360360 ScrollView (Static (self .metrics_text ), id = "modal_metrics" )
361361 ),
362362 Horizontal (
363- Button ("Kapat " , id = "modal_close" , variant = "primary" ),
363+ Button ("Close " , id = "modal_close" , variant = "primary" ),
364364 ),
365365 )
366-
366+
367367if __name__ == "__main__" :
368368 PatternAnalyzerTUI ().run ()
0 commit comments