@@ -87,6 +87,7 @@ def __init__(self, packet_summary: str, packet_text: str, parent=None):
8787 self .setCentralWidget (container )
8888
8989 def append_token (self , token : str ):
90+ """Append a streamed token to the output, clearing the placeholder on first call."""
9091 if self ._output .toPlainText () == "Analysing…" :
9192 self ._output .clear ()
9293 cursor = self ._output .textCursor ()
@@ -96,13 +97,16 @@ def append_token(self, token: str):
9697 self ._output .ensureCursorVisible ()
9798
9899 def set_analysing (self ):
100+ """Reset the output area to the 'analysing' placeholder state."""
99101 self ._output .setPlainText ("Analysing…" )
100102 self ._copy_button .setEnabled (False )
101103
102104 def set_error (self , msg : str ):
105+ """Display an error message in the output area."""
103106 self ._output .setPlainText (f"Error: { msg } " )
104107
105108 def set_done (self ):
109+ """Mark analysis as complete and enable the copy button."""
106110 self ._copy_button .setEnabled (True )
107111
108112 def _copy_to_clipboard (self ):
@@ -176,6 +180,44 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
176180 self ._save_pcap_button .setEnabled (False )
177181 layout .addWidget (self ._save_pcap_button )
178182
183+ self ._build_packet_panel (layout )
184+
185+ self ._captured_packets = [] # newest first, mirrors list order
186+ self ._ollama_thread : OllamaThread | None = None
187+ self ._llm_window : LlmAnalysisWindow | None = None
188+
189+ central_widget = QWidget ()
190+ central_widget .setLayout (layout )
191+ self .setCentralWidget (central_widget )
192+ self .resize (680 , 850 )
193+
194+ self ._load_ollama_models ()
195+
196+ @Slot (bool )
197+ def _toggle_mitm (self , checked ):
198+ if checked :
199+ self ._mitm = MitmThread (self .interface , self .ip_address , self .gateway_ip )
200+ self ._mitm .statusChanged .connect (self ._on_status )
201+ self ._mitm .packetCaptured .connect (self ._on_packet )
202+ self ._mitm .stopped .connect (self ._on_mitm_stopped )
203+ self ._mitm .start ()
204+ self ._mitm_button .setText ("Stop MITM" )
205+ self ._status_label .setStyleSheet ("color: #ff8800; font-weight: bold" )
206+ else :
207+ self ._mitm_button .setEnabled (False )
208+ if self ._mitm :
209+ self ._mitm .stop () # async — _on_mitm_stopped will reset UI
210+
211+ @Slot ()
212+ def _on_mitm_stopped (self ):
213+ self ._mitm = None
214+ self ._mitm_button .setText ("Start MITM" )
215+ self ._mitm_button .setChecked (False )
216+ self ._mitm_button .setEnabled (True )
217+ self ._status_label .setStyleSheet ("color: grey" )
218+
219+ def _build_packet_panel (self , layout : QVBoxLayout ):
220+ """Build the packet list/detail/LLM controls and add them to *layout*."""
179221 mono = QFont ()
180222 mono .setStyleHint (QFont .StyleHint .Monospace )
181223 mono .setPointSize (9 )
@@ -194,7 +236,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
194236 self ._analyse_button .setEnabled (False )
195237 self ._analyse_button .clicked .connect (self ._analyse_packet )
196238
197- # Stack: packet list | packet detail | [analyse btn + llm output]
198239 pkt_splitter = QSplitter (Qt .Vertical )
199240 pkt_splitter .addWidget (self ._packet_list )
200241 pkt_splitter .addWidget (self ._packet_detail )
@@ -206,7 +247,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
206247 )
207248 self ._user_context .returnPressed .connect (self ._analyse_packet )
208249
209- # Model selector row
210250 self ._model_combo = QComboBox ()
211251 self ._model_combo .setPlaceholderText ("Select Ollama model…" )
212252 self ._refresh_models_button = QPushButton ("↻" )
@@ -225,40 +265,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
225265 layout .addLayout (model_row )
226266 layout .addWidget (self ._analyse_button )
227267
228- self ._captured_packets = [] # newest first, mirrors list order
229- self ._ollama_thread : OllamaThread | None = None
230- self ._llm_window : LlmAnalysisWindow | None = None
231-
232- central_widget = QWidget ()
233- central_widget .setLayout (layout )
234- self .setCentralWidget (central_widget )
235- self .resize (680 , 850 )
236-
237- self ._load_ollama_models ()
238-
239- @Slot (bool )
240- def _toggle_mitm (self , checked ):
241- if checked :
242- self ._mitm = MitmThread (self .interface , self .ip_address , self .gateway_ip )
243- self ._mitm .statusChanged .connect (self ._on_status )
244- self ._mitm .packetCaptured .connect (self ._on_packet )
245- self ._mitm .stopped .connect (self ._on_mitm_stopped )
246- self ._mitm .start ()
247- self ._mitm_button .setText ("Stop MITM" )
248- self ._status_label .setStyleSheet ("color: #ff8800; font-weight: bold" )
249- else :
250- self ._mitm_button .setEnabled (False )
251- if self ._mitm :
252- self ._mitm .stop () # async — _on_mitm_stopped will reset UI
253-
254- @Slot ()
255- def _on_mitm_stopped (self ):
256- self ._mitm = None
257- self ._mitm_button .setText ("Start MITM" )
258- self ._mitm_button .setChecked (False )
259- self ._mitm_button .setEnabled (True )
260- self ._status_label .setStyleSheet ("color: grey" )
261-
262268 def _load_ollama_models (self ):
263269 """Populate the model combo box with models available on the local Ollama server."""
264270 models = fetch_ollama_models ()
@@ -684,8 +690,6 @@ def _shutdown(self):
684690 if self .arp_scanner_thread is not None and self .arp_scanner_thread .isRunning ():
685691 self .arp_scanner_thread .quit ()
686692 self .arp_scanner_thread .wait ()
687- from PySide6 .QtWidgets import QApplication # pylint: disable=E0611
688-
689693 QApplication .quit ()
690694
691695
@@ -744,7 +748,10 @@ def run(self):
744748 def _scan_network (self , src_ip , network ):
745749 hosts = [str (ip ) for ip in network .hosts () if str (ip ) != src_ip ]
746750 if self .use_native :
747- print (f"[scan] native ARP scanner — { len (hosts )} hosts timeout={ self .timeout } s workers={ _RESOLVE_WORKERS } " )
751+ print (
752+ f"[scan] native ARP scanner — { len (hosts )} hosts"
753+ f" timeout={ self .timeout } s workers={ _RESOLVE_WORKERS } "
754+ )
748755 return self ._run_native_scan (src_ip , hosts )
749756 print (f"Using Scapy ARP scanner — { len (hosts )} hosts" )
750757 return self ._run_scapy_scan (hosts )
0 commit comments