vector-inspector 0.3.4__py3-none-any.whl → 0.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,21 @@
1
1
  """Connection configuration view."""
2
2
 
3
3
  from PySide6.QtWidgets import (
4
- QWidget, QVBoxLayout, QHBoxLayout, QLabel,
5
- QPushButton, QDialog, QFormLayout, QLineEdit,
6
- QRadioButton, QButtonGroup, QGroupBox, QFileDialog, QComboBox, QApplication, QCheckBox
4
+ QWidget,
5
+ QVBoxLayout,
6
+ QHBoxLayout,
7
+ QLabel,
8
+ QPushButton,
9
+ QDialog,
10
+ QFormLayout,
11
+ QLineEdit,
12
+ QRadioButton,
13
+ QButtonGroup,
14
+ QGroupBox,
15
+ QFileDialog,
16
+ QComboBox,
17
+ QApplication,
18
+ QCheckBox,
7
19
  )
8
20
  from PySide6.QtCore import Signal, QThread
9
21
 
@@ -11,19 +23,20 @@ from vector_inspector.core.connections.base_connection import VectorDBConnection
11
23
  from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
12
24
  from vector_inspector.core.connections.qdrant_connection import QdrantConnection
13
25
  from vector_inspector.core.connections.pinecone_connection import PineconeConnection
26
+ from vector_inspector.core.connections.pgvector_connection import PgVectorConnection
14
27
  from vector_inspector.ui.components.loading_dialog import LoadingDialog
15
28
  from vector_inspector.services.settings_service import SettingsService
16
29
 
17
30
 
18
31
  class ConnectionThread(QThread):
19
32
  """Background thread for connecting to database."""
20
-
33
+
21
34
  finished = Signal(bool, list) # success, collections
22
-
35
+
23
36
  def __init__(self, connection):
24
37
  super().__init__()
25
38
  self.connection = connection
26
-
39
+
27
40
  def run(self):
28
41
  """Connect to database and get collections."""
29
42
  try:
@@ -39,70 +52,71 @@ class ConnectionThread(QThread):
39
52
 
40
53
  class ConnectionDialog(QDialog):
41
54
  """Dialog for configuring database connection."""
42
-
55
+
43
56
  def __init__(self, parent=None):
44
57
  super().__init__(parent)
45
58
  self.setWindowTitle("Connect to Vector Database")
46
59
  self.setMinimumWidth(450)
47
-
60
+
48
61
  self.settings_service = SettingsService()
49
-
62
+
50
63
  self.provider = "chromadb"
51
64
  self.connection_type = "persistent"
52
65
  self.path = ""
53
66
  self.host = "localhost"
54
67
  self.port = "8000"
55
-
68
+
56
69
  self._setup_ui()
57
70
  self._load_last_connection()
58
-
71
+
59
72
  def _setup_ui(self):
60
73
  """Setup dialog UI."""
61
74
  layout = QVBoxLayout(self)
62
-
75
+
63
76
  # Provider selection
64
77
  provider_group = QGroupBox("Database Provider")
65
78
  provider_layout = QVBoxLayout()
66
-
79
+
67
80
  self.provider_combo = QComboBox()
68
81
  self.provider_combo.addItem("ChromaDB", "chromadb")
69
82
  self.provider_combo.addItem("Qdrant", "qdrant")
70
83
  self.provider_combo.addItem("Pinecone", "pinecone")
84
+ self.provider_combo.addItem("PgVector/PostgreSQL", "pgvector")
71
85
  self.provider_combo.currentIndexChanged.connect(self._on_provider_changed)
72
86
  provider_layout.addWidget(self.provider_combo)
73
87
  provider_group.setLayout(provider_layout)
74
-
88
+
75
89
  layout.addWidget(provider_group)
76
-
90
+
77
91
  # Connection type selection
78
92
  type_group = QGroupBox("Connection Type")
79
93
  type_layout = QVBoxLayout()
80
-
94
+
81
95
  self.button_group = QButtonGroup()
82
-
96
+
83
97
  self.persistent_radio = QRadioButton("Persistent (Local File)")
84
98
  self.persistent_radio.setChecked(True)
85
99
  self.persistent_radio.toggled.connect(self._on_type_changed)
86
-
100
+
87
101
  self.http_radio = QRadioButton("HTTP (Remote Server)")
88
-
102
+
89
103
  self.ephemeral_radio = QRadioButton("Ephemeral (In-Memory)")
90
-
104
+
91
105
  self.button_group.addButton(self.persistent_radio)
92
106
  self.button_group.addButton(self.http_radio)
93
107
  self.button_group.addButton(self.ephemeral_radio)
94
-
108
+
95
109
  type_layout.addWidget(self.persistent_radio)
96
110
  type_layout.addWidget(self.http_radio)
97
111
  type_layout.addWidget(self.ephemeral_radio)
98
112
  type_group.setLayout(type_layout)
99
-
113
+
100
114
  layout.addWidget(type_group)
101
-
115
+
102
116
  # Connection details
103
117
  details_group = QGroupBox("Connection Details")
104
118
  form_layout = QFormLayout()
105
-
119
+
106
120
  # Path input (for persistent) + Browse button
107
121
  self.path_input = QLineEdit()
108
122
  # Default to user's test data folder
@@ -115,74 +129,105 @@ class ConnectionDialog(QDialog):
115
129
  browse_button.clicked.connect(self._browse_for_path)
116
130
  path_row_layout.addWidget(browse_button)
117
131
  form_layout.addRow("Data Path:", path_row_widget)
118
-
119
- # Host input (for HTTP)
132
+
133
+ # Host input (for HTTP/PgVector)
120
134
  self.host_input = QLineEdit()
121
135
  self.host_input.setText("localhost")
122
136
  self.host_input.setEnabled(False)
123
137
  form_layout.addRow("Host:", self.host_input)
124
-
125
- # Port input (for HTTP)
138
+
139
+ # Port input (for HTTP/PgVector)
126
140
  self.port_input = QLineEdit()
127
141
  self.port_input.setText("8000")
128
142
  self.port_input.setEnabled(False)
129
143
  form_layout.addRow("Port:", self.port_input)
130
-
144
+
145
+ # Database input (for PgVector)
146
+ self.database_input = QLineEdit()
147
+ self.database_input.setText("subtitles")
148
+ self.database_input.setEnabled(False)
149
+ form_layout.addRow("Database:", self.database_input)
150
+
151
+ # User input (for PgVector)
152
+ self.user_input = QLineEdit()
153
+ self.user_input.setText("postgres")
154
+ self.user_input.setEnabled(False)
155
+ form_layout.addRow("User:", self.user_input)
156
+
157
+ # Password input (for PgVector)
158
+ self.password_input = QLineEdit()
159
+ self.password_input.setText("postgres")
160
+ self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
161
+ self.password_input.setEnabled(False)
162
+ form_layout.addRow("Password:", self.password_input)
163
+
131
164
  # API Key input (for Qdrant Cloud)
132
165
  self.api_key_input = QLineEdit()
133
166
  self.api_key_input.setEnabled(False)
134
- self.api_key_input.setEchoMode(QLineEdit.Password)
167
+ self.api_key_input.setEchoMode(QLineEdit.EchoMode.Password)
135
168
  self.api_key_row = form_layout.rowCount()
136
169
  form_layout.addRow("API Key:", self.api_key_input)
137
-
170
+
138
171
  details_group.setLayout(form_layout)
139
172
  layout.addWidget(details_group)
140
-
173
+
141
174
  # Auto-connect option
142
175
  self.auto_connect_check = QCheckBox("Auto-connect on startup")
143
176
  self.auto_connect_check.setChecked(False)
144
177
  layout.addWidget(self.auto_connect_check)
145
-
178
+
146
179
  # Buttons
147
180
  button_layout = QHBoxLayout()
148
-
181
+
149
182
  connect_button = QPushButton("Connect")
150
183
  connect_button.clicked.connect(self.accept)
151
184
  connect_button.setDefault(True)
152
-
185
+
153
186
  cancel_button = QPushButton("Cancel")
154
187
  cancel_button.clicked.connect(self.reject)
155
-
188
+
156
189
  button_layout.addStretch()
157
190
  button_layout.addWidget(connect_button)
158
191
  button_layout.addWidget(cancel_button)
159
-
192
+
160
193
  layout.addLayout(button_layout)
161
194
 
162
195
  # Resolved absolute path preview
163
196
  self.absolute_path_label = QLabel("")
164
197
  self.absolute_path_label.setStyleSheet("color: gray; font-size: 11px;")
165
198
  layout.addWidget(self.absolute_path_label)
166
-
199
+
167
200
  # Update preview when inputs change
168
201
  self.path_input.textChanged.connect(self._update_absolute_preview)
169
202
  self.persistent_radio.toggled.connect(self._update_absolute_preview)
170
203
  self._update_absolute_preview()
171
-
204
+
172
205
  def _on_provider_changed(self):
173
206
  """Handle provider selection change."""
174
207
  self.provider = self.provider_combo.currentData()
175
-
208
+
176
209
  # Update default port based on provider
177
- if self.provider == "qdrant":
178
- if self.port_input.text() == "8000":
179
- self.port_input.setText("6333")
180
- elif self.provider == "chromadb":
181
- if self.port_input.text() == "6333":
182
- self.port_input.setText("8000")
183
-
184
- # For Pinecone, hide persistent/HTTP options and only show API key
185
- if self.provider == "pinecone":
210
+ if self.provider == "qdrant" and self.port_input.text() == "8000":
211
+ self.port_input.setText("6333")
212
+ elif self.provider == "chromadb" and self.port_input.text() == "6333":
213
+ self.port_input.setText("8000")
214
+
215
+ # Enable/disable fields for PgVector
216
+ if self.provider == "pgvector":
217
+ self.persistent_radio.setEnabled(False)
218
+ self.http_radio.setEnabled(False)
219
+ self.ephemeral_radio.setEnabled(False)
220
+ self.path_input.setEnabled(False)
221
+ self.host_input.setEnabled(True)
222
+ self.port_input.setEnabled(True)
223
+ self.database_input.setEnabled(True)
224
+ self.user_input.setEnabled(True)
225
+ self.password_input.setEnabled(True)
226
+ self.api_key_input.setEnabled(False)
227
+ # Set default port for PostgreSQL if not set
228
+ if self.port_input.text() in ("8000", "6333"):
229
+ self.port_input.setText("5432")
230
+ elif self.provider == "pinecone":
186
231
  self.persistent_radio.setEnabled(False)
187
232
  self.http_radio.setEnabled(True)
188
233
  self.http_radio.setChecked(True)
@@ -191,6 +236,9 @@ class ConnectionDialog(QDialog):
191
236
  self.host_input.setEnabled(False)
192
237
  self.port_input.setEnabled(False)
193
238
  self.api_key_input.setEnabled(True)
239
+ self.database_input.setEnabled(False)
240
+ self.user_input.setEnabled(False)
241
+ self.password_input.setEnabled(False)
194
242
  else:
195
243
  self.persistent_radio.setEnabled(True)
196
244
  self.http_radio.setEnabled(True)
@@ -200,57 +248,84 @@ class ConnectionDialog(QDialog):
200
248
  self.api_key_input.setEnabled(is_http and self.provider == "qdrant")
201
249
  # Update path/host/port based on connection type
202
250
  self._on_type_changed()
203
-
251
+ # Disable PgVector fields for other providers
252
+ self.database_input.setEnabled(False)
253
+ self.user_input.setEnabled(False)
254
+ self.password_input.setEnabled(False)
255
+
204
256
  def _on_type_changed(self):
205
257
  """Handle connection type change."""
206
258
  is_persistent = self.persistent_radio.isChecked()
207
259
  is_http = self.http_radio.isChecked()
208
-
260
+
209
261
  # Pinecone always uses API key, no path/host/port
210
262
  if self.provider == "pinecone":
211
263
  self.path_input.setEnabled(False)
212
264
  self.host_input.setEnabled(False)
213
265
  self.port_input.setEnabled(False)
214
266
  self.api_key_input.setEnabled(True)
267
+ self.database_input.setEnabled(False)
268
+ self.user_input.setEnabled(False)
269
+ self.password_input.setEnabled(False)
270
+ elif self.provider == "pgvector":
271
+ self.path_input.setEnabled(False)
272
+ self.host_input.setEnabled(True)
273
+ self.port_input.setEnabled(True)
274
+ self.database_input.setEnabled(True)
275
+ self.user_input.setEnabled(True)
276
+ self.password_input.setEnabled(True)
277
+ self.api_key_input.setEnabled(False)
215
278
  else:
216
279
  self.path_input.setEnabled(is_persistent)
217
280
  self.host_input.setEnabled(is_http)
218
281
  self.port_input.setEnabled(is_http)
219
282
  self.api_key_input.setEnabled(is_http and self.provider == "qdrant")
283
+ self.database_input.setEnabled(False)
284
+ self.user_input.setEnabled(False)
285
+ self.password_input.setEnabled(False)
220
286
 
221
287
  self._update_absolute_preview()
222
-
288
+
223
289
  def get_connection_config(self):
224
290
  """Get connection configuration from dialog."""
225
291
  # Get current provider from combo box to ensure it's up to date
226
292
  self.provider = self.provider_combo.currentData()
227
-
293
+
228
294
  config = {"provider": self.provider}
229
-
230
- # Pinecone only needs API key
295
+
231
296
  if self.provider == "pinecone":
232
- config.update({
233
- "type": "cloud",
234
- "api_key": self.api_key_input.text()
235
- })
297
+ config.update({"type": "cloud", "api_key": self.api_key_input.text()})
298
+ elif self.provider == "pgvector":
299
+ config.update(
300
+ {
301
+ "type": "pgvector",
302
+ "host": self.host_input.text(),
303
+ "port": int(self.port_input.text()),
304
+ "database": self.database_input.text(),
305
+ "user": self.user_input.text(),
306
+ "password": self.password_input.text(),
307
+ }
308
+ )
236
309
  elif self.persistent_radio.isChecked():
237
310
  config.update({"type": "persistent", "path": self.path_input.text()})
238
311
  elif self.http_radio.isChecked():
239
- config.update({
240
- "type": "http",
241
- "host": self.host_input.text(),
242
- "port": int(self.port_input.text()),
243
- "api_key": self.api_key_input.text() if self.api_key_input.text() else None
244
- })
312
+ config.update(
313
+ {
314
+ "type": "http",
315
+ "host": self.host_input.text(),
316
+ "port": int(self.port_input.text()),
317
+ "api_key": self.api_key_input.text() if self.api_key_input.text() else None,
318
+ }
319
+ )
245
320
  else:
246
321
  config.update({"type": "ephemeral"})
247
-
322
+
248
323
  # Save auto-connect preference
249
324
  config["auto_connect"] = self.auto_connect_check.isChecked()
250
-
325
+
251
326
  # Save this configuration for next time
252
327
  self.settings_service.save_last_connection(config)
253
-
328
+
254
329
  return config
255
330
 
256
331
  def _update_absolute_preview(self):
@@ -261,6 +336,7 @@ class ConnectionDialog(QDialog):
261
336
  rel = self.path_input.text().strip() or "."
262
337
  # Resolve relative to project root by searching for pyproject.toml
263
338
  from pathlib import Path
339
+
264
340
  current = Path(__file__).resolve()
265
341
  abs_path = None
266
342
  for parent in current.parents:
@@ -276,6 +352,7 @@ class ConnectionDialog(QDialog):
276
352
  # Suggest current resolved path as starting point
277
353
  start_dir = None
278
354
  from pathlib import Path
355
+
279
356
  rel = self.path_input.text().strip() or "."
280
357
  current = Path(__file__).resolve()
281
358
  for parent in current.parents:
@@ -284,7 +361,9 @@ class ConnectionDialog(QDialog):
284
361
  break
285
362
  if start_dir is None:
286
363
  start_dir = str(Path(rel).resolve())
287
- directory = QFileDialog.getExistingDirectory(self, "Select ChromaDB Data Directory", start_dir)
364
+ directory = QFileDialog.getExistingDirectory(
365
+ self, "Select ChromaDB Data Directory", start_dir
366
+ )
288
367
  if directory:
289
368
  # Set as relative to project root if within it, else absolute
290
369
  proj_root = None
@@ -302,19 +381,19 @@ class ConnectionDialog(QDialog):
302
381
  display_path = str(dir_path)
303
382
  self.path_input.setText(display_path)
304
383
  self._update_absolute_preview()
305
-
384
+
306
385
  def _load_last_connection(self):
307
386
  """Load and populate the last connection configuration."""
308
387
  last_config = self.settings_service.get_last_connection()
309
388
  if not last_config:
310
389
  return
311
-
390
+
312
391
  # Set provider
313
392
  provider = last_config.get("provider", "chromadb")
314
393
  index = self.provider_combo.findData(provider)
315
394
  if index >= 0:
316
395
  self.provider_combo.setCurrentIndex(index)
317
-
396
+
318
397
  # Set connection type
319
398
  conn_type = last_config.get("type", "persistent")
320
399
  if conn_type == "cloud":
@@ -323,6 +402,13 @@ class ConnectionDialog(QDialog):
323
402
  api_key = last_config.get("api_key")
324
403
  if api_key:
325
404
  self.api_key_input.setText(api_key)
405
+ elif conn_type == "pgvector":
406
+ # PgVector connection
407
+ self.host_input.setText(last_config.get("host", "localhost"))
408
+ self.port_input.setText(str(last_config.get("port", "5432")))
409
+ self.database_input.setText(last_config.get("database", "subtitles"))
410
+ self.user_input.setText(last_config.get("user", "postgres"))
411
+ self.password_input.setText(last_config.get("password", "postgres"))
326
412
  elif conn_type == "persistent":
327
413
  self.persistent_radio.setChecked(True)
328
414
  path = last_config.get("path", "")
@@ -339,7 +425,7 @@ class ConnectionDialog(QDialog):
339
425
  self.api_key_input.setText(api_key)
340
426
  elif conn_type == "ephemeral":
341
427
  self.ephemeral_radio.setChecked(True)
342
-
428
+
343
429
  # Set auto-connect checkbox
344
430
  auto_connect = last_config.get("auto_connect", False)
345
431
  self.auto_connect_check.setChecked(auto_connect)
@@ -347,10 +433,10 @@ class ConnectionDialog(QDialog):
347
433
 
348
434
  class ConnectionView(QWidget):
349
435
  """Widget for managing database connection."""
350
-
436
+
351
437
  connection_changed = Signal(bool)
352
438
  connection_created = Signal(VectorDBConnection) # Signal when new connection is created
353
-
439
+
354
440
  def __init__(self, connection: VectorDBConnection, parent=None):
355
441
  super().__init__(parent)
356
442
  self.connection = connection
@@ -358,61 +444,64 @@ class ConnectionView(QWidget):
358
444
  self.settings_service = SettingsService()
359
445
  self.connection_thread = None
360
446
  self._setup_ui()
361
-
447
+
362
448
  # Try to auto-connect if enabled in settings
363
449
  self._try_auto_connect()
364
-
450
+
365
451
  def _setup_ui(self):
366
452
  """Setup widget UI."""
367
453
  layout = QVBoxLayout(self)
368
454
  layout.setContentsMargins(0, 0, 0, 0)
369
-
455
+
370
456
  # Connection status group
371
457
  group = QGroupBox("Connection")
372
458
  group_layout = QVBoxLayout()
373
-
459
+
374
460
  self.status_label = QLabel("Status: Not connected")
375
461
  group_layout.addWidget(self.status_label)
376
-
462
+
377
463
  # Button layout with both connect and disconnect
378
464
  button_layout = QHBoxLayout()
379
-
465
+
380
466
  self.connect_button = QPushButton("Connect")
381
467
  self.connect_button.clicked.connect(self.show_connection_dialog)
382
468
  button_layout.addWidget(self.connect_button)
383
-
469
+
384
470
  self.disconnect_button = QPushButton("Disconnect")
385
471
  self.disconnect_button.clicked.connect(self._disconnect)
386
472
  self.disconnect_button.setEnabled(False)
387
473
  button_layout.addWidget(self.disconnect_button)
388
-
474
+
389
475
  group_layout.addLayout(button_layout)
390
-
476
+
391
477
  group.setLayout(group_layout)
392
478
  layout.addWidget(group)
393
-
479
+
394
480
  def show_connection_dialog(self):
395
481
  """Show connection configuration dialog."""
396
482
  dialog = ConnectionDialog(self)
397
-
398
- if dialog.exec() == QDialog.Accepted:
483
+
484
+ if dialog.exec() == QDialog.DialogCode.Accepted:
399
485
  config = dialog.get_connection_config()
400
486
  self._connect_with_config(config)
401
-
487
+
402
488
  def _connect_with_config(self, config: dict):
403
489
  """Connect to database with given configuration."""
404
490
  self.loading_dialog.show_loading("Connecting to database...")
405
-
491
+
406
492
  provider = config.get("provider", "chromadb")
407
493
  conn_type = config.get("type")
408
-
494
+
409
495
  # Create appropriate connection instance based on provider
410
496
  if provider == "pinecone":
411
497
  api_key = config.get("api_key")
412
498
  if not api_key:
413
499
  self.loading_dialog.hide_loading()
414
500
  from PySide6.QtWidgets import QMessageBox
415
- QMessageBox.warning(self, "Missing API Key", "Pinecone requires an API key to connect.")
501
+
502
+ QMessageBox.warning(
503
+ self, "Missing API Key", "Pinecone requires an API key to connect."
504
+ )
416
505
  return
417
506
  self.connection = PineconeConnection(api_key=api_key)
418
507
  elif provider == "qdrant":
@@ -420,61 +509,71 @@ class ConnectionView(QWidget):
420
509
  self.connection = QdrantConnection(path=config.get("path"))
421
510
  elif conn_type == "http":
422
511
  self.connection = QdrantConnection(
423
- host=config.get("host"),
424
- port=config.get("port"),
425
- api_key=config.get("api_key")
512
+ host=config.get("host"), port=config.get("port"), api_key=config.get("api_key")
426
513
  )
427
514
  else: # ephemeral/memory
428
515
  self.connection = QdrantConnection()
516
+ elif provider == "pgvector":
517
+ self.connection = PgVectorConnection(
518
+ host=config.get("host", "localhost"),
519
+ port=config.get("port", 5432),
520
+ database=config.get("database", "subtitles"),
521
+ user=config.get("user", "postgres"),
522
+ password=config.get("password", "postgres"),
523
+ )
429
524
  else: # chromadb
430
525
  if conn_type == "persistent":
431
526
  self.connection = ChromaDBConnection(path=config.get("path"))
432
527
  elif conn_type == "http":
433
528
  self.connection = ChromaDBConnection(
434
- host=config.get("host"),
435
- port=config.get("port")
529
+ host=config.get("host"), port=config.get("port")
436
530
  )
437
531
  else: # ephemeral
438
532
  self.connection = ChromaDBConnection()
439
-
533
+
440
534
  # Store config for later use
441
535
  self._pending_config = config
442
-
536
+
443
537
  # Notify parent that connection instance changed
444
538
  self.connection_created.emit(self.connection)
445
-
539
+
446
540
  # Start background thread to connect
447
541
  self.connection_thread = ConnectionThread(self.connection)
448
542
  self.connection_thread.finished.connect(self._on_connection_finished)
449
543
  self.connection_thread.start()
450
-
544
+
451
545
  def _on_connection_finished(self, success: bool, collections: list):
452
546
  """Handle connection thread completion."""
453
547
  self.loading_dialog.hide_loading()
454
-
548
+
455
549
  if success:
456
550
  config = self._pending_config
457
551
  provider = config.get("provider", "chromadb")
458
-
552
+
459
553
  # Show provider, path/host + collection count for clarity
460
- details = []
461
- details.append(f"provider: {provider}")
462
- if hasattr(self.connection, 'path') and self.connection.path:
463
- details.append(f"path: {self.connection.path}")
464
- if hasattr(self.connection, 'host') and self.connection.host:
465
- port = getattr(self.connection, 'port', None)
466
- details.append(f"host: {self.connection.host}:{port}")
554
+ details = [f"provider: {provider}"]
555
+ # Show path for persistent ChromaDB/Qdrant
556
+ if provider in ("chromadb", "qdrant") and hasattr(self.connection, "path"):
557
+ path = getattr(self.connection, "path", None)
558
+ if path:
559
+ details.append(f"path: {path}")
560
+ # Show host/port for HTTP or PgVector
561
+ if provider in ("qdrant", "chromadb", "pgvector") and hasattr(self.connection, "host"):
562
+ host = getattr(self.connection, "host", None)
563
+ port = getattr(self.connection, "port", None)
564
+ if host:
565
+ details.append(f"host: {host}:{port}")
467
566
  count_text = f"collections: {len(collections)}"
468
567
  info = ", ".join(details)
469
568
  self.status_label.setText(f"Status: Connected ({info}, {count_text})")
470
-
569
+
471
570
  # Enable disconnect, disable connect
472
571
  self.connect_button.setEnabled(False)
473
572
  self.disconnect_button.setEnabled(True)
474
-
573
+
475
574
  # Emit signal which triggers collection browser refresh
476
575
  self.connection_changed.emit(True)
477
-
576
+
478
577
  # Process events to ensure collection browser is updated
479
578
  QApplication.processEvents()
480
579
  else:
@@ -483,18 +582,18 @@ class ConnectionView(QWidget):
483
582
  self.connect_button.setEnabled(True)
484
583
  self.disconnect_button.setEnabled(False)
485
584
  self.connection_changed.emit(False)
486
-
585
+
487
586
  def _disconnect(self):
488
587
  """Disconnect from database."""
489
588
  self.connection.disconnect()
490
589
  self.status_label.setText("Status: Not connected")
491
-
590
+
492
591
  # Enable connect, disable disconnect
493
592
  self.connect_button.setEnabled(True)
494
593
  self.disconnect_button.setEnabled(False)
495
-
594
+
496
595
  self.connection_changed.emit(False)
497
-
596
+
498
597
  def _try_auto_connect(self):
499
598
  """Try to automatically connect if auto-connect is enabled."""
500
599
  last_config = self.settings_service.get_last_connection()