1616#include < QLabel>
1717#include < QLineEdit>
1818#include < QListWidget>
19+ #include < QMessageBox>
1920#include < QPlainTextEdit>
2021#include < QPushButton>
22+ #include < QSet>
2123#include < QSpinBox>
2224#include < QSplitter>
25+ #include < QTcpSocket>
26+ #include < QHostAddress>
2327#include < QTimer>
2428#include < QUuid>
2529#include < QVBoxLayout>
@@ -134,6 +138,28 @@ EditMiniAppsDialog::EditMiniAppsDialog(MiniAppRegistry *registry,
134138 advLayout->addWidget (m_timeoutSpin);
135139 formLayout->addWidget (m_advancedGroup);
136140
141+ // Debug section (collapsible)
142+ m_debugGroup = new QGroupBox (tr (" Debug" ), rightWidget);
143+ m_debugGroup->setCheckable (true );
144+ m_debugGroup->setChecked (false );
145+ auto *debugLayout = new QVBoxLayout (m_debugGroup);
146+ debugLayout->addWidget (new QLabel (tr (" CDP Debug Port:" ), m_debugGroup));
147+ auto *portLayout = new QHBoxLayout;
148+ m_debugPortSpin = new QSpinBox (m_debugGroup);
149+ m_debugPortSpin->setRange (0 , 65535 );
150+ m_debugPortSpin->setSpecialValueText (tr (" Disabled" ));
151+ m_debugPortSpin->setToolTip (tr (" 0 = disabled. Set a port to enable Chrome DevTools Protocol debugging." ));
152+ portLayout->addWidget (m_debugPortSpin, 1 );
153+ m_randomPortBtn = new QPushButton (tr (" Random" ), m_debugGroup);
154+ m_randomPortBtn->setToolTip (tr (" Find an available port in range 9222-9322" ));
155+ portLayout->addWidget (m_randomPortBtn);
156+ debugLayout->addLayout (portLayout);
157+ m_portWarningLabel = new QLabel (m_debugGroup);
158+ m_portWarningLabel->setStyleSheet (QStringLiteral (" color: orange; font-size: 10px;" ));
159+ m_portWarningLabel->hide ();
160+ debugLayout->addWidget (m_portWarningLabel);
161+ formLayout->addWidget (m_debugGroup);
162+
137163 formLayout->addStretch ();
138164
139165 splitter->addWidget (leftWidget);
@@ -158,10 +184,12 @@ EditMiniAppsDialog::EditMiniAppsDialog(MiniAppRegistry *registry,
158184 connect (m_upBtn, &QPushButton::clicked, this , &EditMiniAppsDialog::onMoveUpClicked);
159185 connect (m_downBtn, &QPushButton::clicked, this , &EditMiniAppsDialog::onMoveDownClicked);
160186 connect (m_browseCwdBtn, &QPushButton::clicked, this , &EditMiniAppsDialog::onBrowseCwdClicked);
187+ connect (m_randomPortBtn, &QPushButton::clicked, this , &EditMiniAppsDialog::onRandomPortClicked);
161188 connect (m_scopeCombo, QOverload<int >::of (&QComboBox::currentIndexChanged),
162189 this , &EditMiniAppsDialog::onScopeChanged);
163190 connect (m_urlEdit, &QLineEdit::textChanged, this , [this ]() { m_validateTimer->start (); });
164191 connect (m_envEdit, &QPlainTextEdit::textChanged, this , [this ]() { m_validateTimer->start (); });
192+ connect (m_debugPortSpin, QOverload<int >::of (&QSpinBox::valueChanged), this , [this ]() { m_validateTimer->start (); });
165193
166194 connect (m_buttonBox, &QDialogButtonBox::accepted, this , [this ]() {
167195 commitCurrentApp ();
@@ -248,6 +276,7 @@ void EditMiniAppsDialog::commitCurrentApp()
248276 def.env = m_envEdit->toPlainText ();
249277 def.healthCheckUrl = m_healthUrlEdit->text ().trimmed ();
250278 def.healthTimeoutMs = m_timeoutSpin->value () * 1000 ;
279+ def.debugPort = m_debugGroup->isChecked () ? m_debugPortSpin->value () : 0 ;
251280
252281 // Ensure ID
253282 if (def.id .isEmpty ())
@@ -271,6 +300,8 @@ void EditMiniAppsDialog::loadApp(int row)
271300 m_envEdit->setEnabled (valid);
272301 m_healthUrlEdit->setEnabled (valid);
273302 m_timeoutSpin->setEnabled (valid);
303+ m_debugPortSpin->setEnabled (valid);
304+ m_randomPortBtn->setEnabled (valid);
274305
275306 if (!valid) {
276307 m_nameEdit->clear ();
@@ -280,8 +311,10 @@ void EditMiniAppsDialog::loadApp(int row)
280311 m_envEdit->clear ();
281312 m_healthUrlEdit->clear ();
282313 m_timeoutSpin->setValue (30 );
314+ m_debugPortSpin->setValue (0 );
283315 m_urlWarningLabel->hide ();
284316 m_envWarningLabel->hide ();
317+ m_portWarningLabel->hide ();
285318 return ;
286319 }
287320
@@ -293,7 +326,10 @@ void EditMiniAppsDialog::loadApp(int row)
293326 m_envEdit->setPlainText (def.env );
294327 m_healthUrlEdit->setText (def.healthCheckUrl );
295328 m_timeoutSpin->setValue (def.healthTimeoutMs / 1000 );
329+ m_debugPortSpin->setValue (def.debugPort );
330+ m_debugGroup->setChecked (def.debugPort > 0 );
296331 m_urlWarningLabel->hide ();
332+ m_portWarningLabel->hide ();
297333 validateFields ();
298334}
299335
@@ -372,6 +408,26 @@ void EditMiniAppsDialog::validateFields()
372408 m_urlWarningLabel->hide ();
373409 }
374410
411+ // Debug port conflict validation
412+ const int port = m_debugPortSpin->value ();
413+ if (port > 0 && m_currentRow >= 0 ) {
414+ bool conflict = false ;
415+ for (int i = 0 ; i < m_apps.size (); ++i) {
416+ if (i == m_currentRow) continue ;
417+ if (m_apps[i].debugPort == port) {
418+ m_portWarningLabel->setText (tr (" Port %1 is already used by \" %2\" " )
419+ .arg (port).arg (m_apps[i].name ));
420+ m_portWarningLabel->show ();
421+ conflict = true ;
422+ break ;
423+ }
424+ }
425+ if (!conflict)
426+ m_portWarningLabel->hide ();
427+ } else {
428+ m_portWarningLabel->hide ();
429+ }
430+
375431 // Env validation (same as EditTasksDialog)
376432 const QString envText = m_envEdit->toPlainText ();
377433 if (envText.trimmed ().isEmpty ()) {
@@ -405,3 +461,32 @@ void EditMiniAppsDialog::updateButtonStates()
405461 m_upBtn->setEnabled (row > 0 );
406462 m_downBtn->setEnabled (row >= 0 && row < count - 1 );
407463}
464+
465+ void EditMiniAppsDialog::onRandomPortClicked ()
466+ {
467+ // Collect ports already assigned to other apps in the list
468+ QSet<int > usedPorts;
469+ for (int i = 0 ; i < m_apps.size (); ++i) {
470+ if (i == m_currentRow) continue ;
471+ if (m_apps[i].debugPort > 0 )
472+ usedPorts.insert (m_apps[i].debugPort );
473+ }
474+
475+ // Scan 9222-9322 for an available port
476+ for (int port = 9222 ; port <= 9322 ; ++port) {
477+ if (usedPorts.contains (port))
478+ continue ;
479+
480+ // Bind-test: check if port is actually free on the system
481+ QTcpSocket sock;
482+ if (sock.bind (QHostAddress::LocalHost, static_cast <quint16>(port))) {
483+ sock.close ();
484+ m_debugPortSpin->setValue (port);
485+ return ;
486+ }
487+ }
488+
489+ // All ports exhausted
490+ QMessageBox::warning (this , tr (" No Available Port" ),
491+ tr (" No available port in range 9222-9322. Please enter a port manually." ));
492+ }
0 commit comments