vector-inspector 0.3.7__tar.gz → 0.3.8__tar.gz

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.
Files changed (80) hide show
  1. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/PKG-INFO +27 -9
  2. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/README.md +26 -8
  3. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/pyproject.toml +1 -1
  4. vector_inspector-0.3.8/src/vector_inspector/core/provider_factory.py +97 -0
  5. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/splash_window.py +14 -2
  6. vector_inspector-0.3.8/src/vector_inspector/ui/controllers/__init__.py +1 -0
  7. vector_inspector-0.3.8/src/vector_inspector/ui/controllers/connection_controller.py +177 -0
  8. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/main_window.py +85 -332
  9. vector_inspector-0.3.8/src/vector_inspector/ui/main_window_shell.py +106 -0
  10. vector_inspector-0.3.8/src/vector_inspector/ui/services/__init__.py +1 -0
  11. vector_inspector-0.3.8/src/vector_inspector/ui/services/dialog_service.py +113 -0
  12. vector_inspector-0.3.8/src/vector_inspector/ui/tabs.py +64 -0
  13. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/__init__.py +0 -0
  14. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/__main__.py +0 -0
  15. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/config/__init__.py +0 -0
  16. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/config/known_embedding_models.json +0 -0
  17. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/__init__.py +0 -0
  18. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/cache_manager.py +0 -0
  19. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connection_manager.py +0 -0
  20. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/__init__.py +0 -0
  21. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/base_connection.py +0 -0
  22. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/chroma_connection.py +0 -0
  23. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/pgvector_connection.py +0 -0
  24. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/pinecone_connection.py +0 -0
  25. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/qdrant_connection.py +0 -0
  26. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/qdrant_helpers/__init__.py +0 -0
  27. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/qdrant_helpers/qdrant_embedding_resolver.py +0 -0
  28. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/qdrant_helpers/qdrant_filter_builder.py +0 -0
  29. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/connections/template_connection.py +0 -0
  30. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_providers/__init__.py +0 -0
  31. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_providers/base_provider.py +0 -0
  32. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_providers/clip_provider.py +0 -0
  33. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_providers/provider_factory.py +0 -0
  34. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_providers/sentence_transformer_provider.py +0 -0
  35. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/embedding_utils.py +0 -0
  36. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/logging.py +0 -0
  37. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/core/model_registry.py +0 -0
  38. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/main.py +0 -0
  39. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/__init__.py +0 -0
  40. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/backup_helpers.py +0 -0
  41. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/backup_restore_service.py +0 -0
  42. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/credential_service.py +0 -0
  43. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/filter_service.py +0 -0
  44. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/import_export_service.py +0 -0
  45. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/profile_service.py +0 -0
  46. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/settings_service.py +0 -0
  47. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/update_service.py +0 -0
  48. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/services/visualization_service.py +0 -0
  49. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/__init__.py +0 -0
  50. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/__init__.py +0 -0
  51. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/backup_restore_dialog.py +0 -0
  52. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/connection_manager_panel.py +0 -0
  53. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/filter_builder.py +0 -0
  54. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/item_dialog.py +0 -0
  55. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/loading_dialog.py +0 -0
  56. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/profile_manager_panel.py +0 -0
  57. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/components/update_details_dialog.py +0 -0
  58. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/dialogs/__init__.py +0 -0
  59. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/dialogs/cross_db_migration.py +0 -0
  60. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/dialogs/embedding_config_dialog.py +0 -0
  61. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/dialogs/provider_type_dialog.py +0 -0
  62. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/__init__.py +0 -0
  63. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/collection_browser.py +0 -0
  64. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/connection_view.py +0 -0
  65. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/info_panel.py +0 -0
  66. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/metadata_view.py +0 -0
  67. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/search_view.py +0 -0
  68. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/ui/views/visualization_view.py +0 -0
  69. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/utils/__init__.py +0 -0
  70. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/utils/lazy_imports.py +0 -0
  71. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/src/vector_inspector/utils/version.py +0 -0
  72. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_backup_helpers.py +0 -0
  73. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_backup_restore_service.py +0 -0
  74. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_chroma_connection.py +0 -0
  75. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_filter_service.py +0 -0
  76. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_pgvector_connection.py +0 -0
  77. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_pinecone_connection.py +0 -0
  78. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_qdrant_connection.py +0 -0
  79. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_runner.py +0 -0
  80. {vector_inspector-0.3.7 → vector_inspector-0.3.8}/tests/test_settings_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vector-inspector
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: A comprehensive desktop application for visualizing, querying, and managing vector database data
5
5
  Author-Email: Anthony Dawson <anthonypdawson+github@gmail.com>
6
6
  License: MIT
@@ -31,13 +31,31 @@ Requires-Dist: pgvector>=0.4.2
31
31
  Description-Content-Type: text/markdown
32
32
 
33
33
  # Latest updates
34
- * Added automatic update notification system:
35
- - Checks for new releases on GitHub once per launch/day
36
- - Shows update indicator in the status bar and Help menu
37
- - Clickable indicator opens a modal with release notes and update instructions
38
- - Manual Check for Update in Help menu
39
- - Version detection now uses installed package metadata for accuracy
40
- * Fixed bug where data browser row disappeared post-edit
34
+
35
+ ## Vector Inspector 2026.01 Release Notes
36
+
37
+ ### Major Refactor and Studio-Ready Architecture
38
+ - Refactored main window into modular components:
39
+ - InspectorShell: reusable UI shell (splitter, tabs, layout)
40
+ - ProviderFactory: centralized connection creation
41
+ - DialogService: dialog management
42
+ - ConnectionController: connection lifecycle and threading
43
+ - InspectorTabs: pluggable tab registry
44
+ - MainWindow now inherits from InspectorShell and is fully reusable as a widget
45
+ - Bootstrap logic is separated from UI logic—Studio can host Inspector as a component
46
+ - Tab system is now pluggable: Studio and Inspector can add, remove, or override tabs via TabDefinition
47
+ - All Inspector UI logic is self-contained; Studio can extend without modifying Inspector code
48
+
49
+ ### Data Browser Improvements
50
+ - Added a checkbox: Generate embeddings on edit (default: checked)
51
+ - When unchecked, editing a row skips embedding regeneration
52
+ - Setting is persisted per user
53
+
54
+ ### Developer and Architecture Notes
55
+ - All modules pass syntax checks and are ready for Studio integration
56
+ - No breaking changes for existing Inspector users
57
+ - Inspector is now a true UI module, not just an application
58
+
41
59
  ---
42
60
 
43
61
  # Vector Inspector
@@ -45,7 +63,7 @@ Description-Content-Type: text/markdown
45
63
  > **Disclaimer:** This tool is currently under active development and is **not production ready**. Not all features have been thoroughly tested and code is released frequently. Use with caution in critical or production environments.
46
64
 
47
65
  [![CI](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml)
48
- [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish.yml)
66
+ [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml)
49
67
 
50
68
  [![PyPI Version](https://img.shields.io/pypi/v/vector-inspector.svg?cacheSeconds=300)](https://pypi.org/project/vector-inspector/)
51
69
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/vector-inspector?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/vector-inspector)
@@ -1,11 +1,29 @@
1
1
  # Latest updates
2
- * Added automatic update notification system:
3
- - Checks for new releases on GitHub once per launch/day
4
- - Shows update indicator in the status bar and Help menu
5
- - Clickable indicator opens a modal with release notes and update instructions
6
- - Manual Check for Update in Help menu
7
- - Version detection now uses installed package metadata for accuracy
8
- * Fixed bug where data browser row disappeared post-edit
2
+
3
+ ## Vector Inspector 2026.01 Release Notes
4
+
5
+ ### Major Refactor and Studio-Ready Architecture
6
+ - Refactored main window into modular components:
7
+ - InspectorShell: reusable UI shell (splitter, tabs, layout)
8
+ - ProviderFactory: centralized connection creation
9
+ - DialogService: dialog management
10
+ - ConnectionController: connection lifecycle and threading
11
+ - InspectorTabs: pluggable tab registry
12
+ - MainWindow now inherits from InspectorShell and is fully reusable as a widget
13
+ - Bootstrap logic is separated from UI logic—Studio can host Inspector as a component
14
+ - Tab system is now pluggable: Studio and Inspector can add, remove, or override tabs via TabDefinition
15
+ - All Inspector UI logic is self-contained; Studio can extend without modifying Inspector code
16
+
17
+ ### Data Browser Improvements
18
+ - Added a checkbox: Generate embeddings on edit (default: checked)
19
+ - When unchecked, editing a row skips embedding regeneration
20
+ - Setting is persisted per user
21
+
22
+ ### Developer and Architecture Notes
23
+ - All modules pass syntax checks and are ready for Studio integration
24
+ - No breaking changes for existing Inspector users
25
+ - Inspector is now a true UI module, not just an application
26
+
9
27
  ---
10
28
 
11
29
  # Vector Inspector
@@ -13,7 +31,7 @@
13
31
  > **Disclaimer:** This tool is currently under active development and is **not production ready**. Not all features have been thoroughly tested and code is released frequently. Use with caution in critical or production environments.
14
32
 
15
33
  [![CI](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml)
16
- [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish.yml)
34
+ [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml)
17
35
 
18
36
  [![PyPI Version](https://img.shields.io/pypi/v/vector-inspector.svg?cacheSeconds=300)](https://pypi.org/project/vector-inspector/)
19
37
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/vector-inspector?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/vector-inspector)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vector-inspector"
3
- version = "0.3.7"
3
+ version = "0.3.8"
4
4
  description = "A comprehensive desktop application for visualizing, querying, and managing vector database data"
5
5
  authors = [
6
6
  { name = "Anthony Dawson", email = "anthonypdawson+github@gmail.com" },
@@ -0,0 +1,97 @@
1
+ """Factory for creating vector database connections from provider configs."""
2
+
3
+ from typing import Dict, Any
4
+ from vector_inspector.core.connections.base_connection import VectorDBConnection
5
+ from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
6
+ from vector_inspector.core.connections.qdrant_connection import QdrantConnection
7
+ from vector_inspector.core.connections.pinecone_connection import PineconeConnection
8
+ from vector_inspector.core.connections.pgvector_connection import PgVectorConnection
9
+
10
+
11
+ class ProviderFactory:
12
+ """Factory for creating database connections from configuration."""
13
+
14
+ @staticmethod
15
+ def create(
16
+ provider: str, config: Dict[str, Any], credentials: Dict[str, Any] = None
17
+ ) -> VectorDBConnection:
18
+ """Create a connection object for the specified provider.
19
+
20
+ Args:
21
+ provider: Provider type (chromadb, qdrant, pinecone, pgvector)
22
+ config: Provider-specific configuration
23
+ credentials: Optional credentials (API keys, passwords, etc.)
24
+
25
+ Returns:
26
+ VectorDBConnection instance
27
+
28
+ Raises:
29
+ ValueError: If provider is unsupported or configuration is invalid
30
+ """
31
+ credentials = credentials or {}
32
+
33
+ if provider == "chromadb":
34
+ return ProviderFactory._create_chroma(config, credentials)
35
+ elif provider == "qdrant":
36
+ return ProviderFactory._create_qdrant(config, credentials)
37
+ elif provider == "pinecone":
38
+ return ProviderFactory._create_pinecone(config, credentials)
39
+ elif provider == "pgvector":
40
+ return ProviderFactory._create_pgvector(config, credentials)
41
+ else:
42
+ raise ValueError(f"Unsupported provider: {provider}")
43
+
44
+ @staticmethod
45
+ def _create_chroma(config: Dict[str, Any], credentials: Dict[str, Any]) -> ChromaDBConnection:
46
+ """Create a ChromaDB connection."""
47
+ conn_type = config.get("type")
48
+
49
+ if conn_type == "persistent":
50
+ return ChromaDBConnection(path=config.get("path"))
51
+ elif conn_type == "http":
52
+ return ChromaDBConnection(host=config.get("host"), port=config.get("port"))
53
+ else: # ephemeral
54
+ return ChromaDBConnection()
55
+
56
+ @staticmethod
57
+ def _create_qdrant(config: Dict[str, Any], credentials: Dict[str, Any]) -> QdrantConnection:
58
+ """Create a Qdrant connection."""
59
+ conn_type = config.get("type")
60
+ api_key = credentials.get("api_key")
61
+
62
+ if conn_type == "persistent":
63
+ return QdrantConnection(path=config.get("path"))
64
+ elif conn_type == "http":
65
+ return QdrantConnection(
66
+ host=config.get("host"), port=config.get("port"), api_key=api_key
67
+ )
68
+ else: # ephemeral
69
+ return QdrantConnection()
70
+
71
+ @staticmethod
72
+ def _create_pinecone(config: Dict[str, Any], credentials: Dict[str, Any]) -> PineconeConnection:
73
+ """Create a Pinecone connection."""
74
+ api_key = credentials.get("api_key")
75
+ if not api_key:
76
+ raise ValueError("Pinecone requires an API key")
77
+
78
+ return PineconeConnection(api_key=api_key)
79
+
80
+ @staticmethod
81
+ def _create_pgvector(config: Dict[str, Any], credentials: Dict[str, Any]) -> PgVectorConnection:
82
+ """Create a PgVector/Postgres connection."""
83
+ conn_type = config.get("type")
84
+
85
+ if conn_type == "http":
86
+ host = config.get("host", "localhost")
87
+ port = int(config.get("port", 5432))
88
+ database = config.get("database")
89
+ user = config.get("user")
90
+ # Prefer password from credentials
91
+ password = credentials.get("password")
92
+
93
+ return PgVectorConnection(
94
+ host=host, port=port, database=database, user=user, password=password
95
+ )
96
+
97
+ raise ValueError("Unsupported connection type for PgVector profile")
@@ -34,10 +34,22 @@ class SplashWindow(QDialog):
34
34
  layout.addWidget(github)
35
35
 
36
36
  # About info (reuse About dialog text)
37
- from vector_inspector.ui.main_window import get_about_html
37
+ from vector_inspector.utils.version import get_app_version
38
38
 
39
39
  about = QTextBrowser()
40
- about.setHtml(get_about_html())
40
+ version = get_app_version()
41
+ version_html = (
42
+ f"<h2>Vector Inspector {version}</h2>" if version else "<h2>Vector Inspector</h2>"
43
+ )
44
+ about_text = (
45
+ version_html + "<p>A comprehensive desktop application for visualizing, "
46
+ "querying, and managing multiple vector databases simultaneously.</p>"
47
+ '<p><a href="https://github.com/anthonypdawson/vector-inspector" style="color:#2980b9;">GitHub Project Page</a></p>'
48
+ "<hr />"
49
+ "<p>Built with PySide6</p>"
50
+ "<p><b>New:</b> Pinecone support!</p>"
51
+ )
52
+ about.setHtml(about_text)
41
53
  about.setOpenExternalLinks(True)
42
54
  about.setMaximumHeight(160)
43
55
  layout.addWidget(about)
@@ -0,0 +1 @@
1
+ """UI controllers for managing application logic."""
@@ -0,0 +1,177 @@
1
+ """Controller for managing connection lifecycle and threading."""
2
+
3
+ from typing import Dict, Optional
4
+ from PySide6.QtCore import QObject, Signal, QThread
5
+ from PySide6.QtWidgets import QMessageBox, QWidget
6
+
7
+ from vector_inspector.core.connection_manager import ConnectionManager, ConnectionState
8
+ from vector_inspector.core.connections.base_connection import VectorDBConnection
9
+ from vector_inspector.core.provider_factory import ProviderFactory
10
+ from vector_inspector.services.profile_service import ProfileService
11
+ from vector_inspector.ui.components.loading_dialog import LoadingDialog
12
+
13
+
14
+ class ConnectionThread(QThread):
15
+ """Background thread for connecting to database."""
16
+
17
+ finished = Signal(bool, list, str) # success, collections, error_message
18
+
19
+ def __init__(self, connection: VectorDBConnection):
20
+ super().__init__()
21
+ self.connection = connection
22
+
23
+ def run(self):
24
+ """Connect to database and get collections."""
25
+ try:
26
+ success = self.connection.connect()
27
+ if success:
28
+ collections = self.connection.list_collections()
29
+ self.finished.emit(True, collections, "")
30
+ else:
31
+ self.finished.emit(False, [], "Connection failed")
32
+ except Exception as e:
33
+ self.finished.emit(False, [], str(e))
34
+
35
+
36
+ class ConnectionController(QObject):
37
+ """Controller for managing connection operations and lifecycle.
38
+
39
+ This handles:
40
+ - Creating connections from profiles
41
+ - Starting connection threads
42
+ - Handling connection results
43
+ - Managing loading dialogs
44
+ - Emitting signals for UI updates
45
+ """
46
+
47
+ connection_completed = Signal(
48
+ str, bool, list, str
49
+ ) # connection_id, success, collections, error
50
+
51
+ def __init__(
52
+ self,
53
+ connection_manager: ConnectionManager,
54
+ profile_service: ProfileService,
55
+ parent: Optional[QWidget] = None,
56
+ ):
57
+ super().__init__(parent)
58
+ self.connection_manager = connection_manager
59
+ self.profile_service = profile_service
60
+ self.parent_widget = parent
61
+
62
+ # State
63
+ self._connection_threads: Dict[str, ConnectionThread] = {}
64
+ self.loading_dialog = LoadingDialog("Loading...", parent)
65
+
66
+ def connect_to_profile(self, profile_id: str) -> bool:
67
+ """Connect to a profile.
68
+
69
+ Args:
70
+ profile_id: ID of the profile to connect to
71
+
72
+ Returns:
73
+ True if connection initiated successfully, False otherwise
74
+ """
75
+ profile_data = self.profile_service.get_profile_with_credentials(profile_id)
76
+ if not profile_data:
77
+ QMessageBox.warning(self.parent_widget, "Error", "Profile not found.")
78
+ return False
79
+
80
+ # Check connection limit
81
+ if self.connection_manager.get_connection_count() >= ConnectionManager.MAX_CONNECTIONS:
82
+ QMessageBox.warning(
83
+ self.parent_widget,
84
+ "Connection Limit",
85
+ f"Maximum number of connections ({ConnectionManager.MAX_CONNECTIONS}) reached. "
86
+ "Please close a connection first.",
87
+ )
88
+ return False
89
+
90
+ # Create connection
91
+ provider = profile_data["provider"]
92
+ config = profile_data["config"]
93
+ credentials = profile_data.get("credentials", {})
94
+
95
+ try:
96
+ # Create connection object using factory
97
+ connection = ProviderFactory.create(provider, config, credentials)
98
+
99
+ # Register with connection manager, using profile_id as connection_id for persistence
100
+ connection_id = self.connection_manager.create_connection(
101
+ name=profile_data["name"],
102
+ provider=provider,
103
+ connection=connection,
104
+ config=config,
105
+ connection_id=profile_data["id"],
106
+ )
107
+
108
+ # Update state to connecting
109
+ self.connection_manager.update_connection_state(
110
+ connection_id, ConnectionState.CONNECTING
111
+ )
112
+
113
+ # Connect in background thread
114
+ thread = ConnectionThread(connection)
115
+ thread.finished.connect(
116
+ lambda success, collections, error: self._on_connection_finished(
117
+ connection_id, success, collections, error
118
+ )
119
+ )
120
+ self._connection_threads[connection_id] = thread
121
+ thread.start()
122
+
123
+ # Show loading dialog
124
+ self.loading_dialog.show_loading(f"Connecting to {profile_data['name']}...")
125
+ return True
126
+
127
+ except Exception as e:
128
+ QMessageBox.critical(
129
+ self.parent_widget, "Connection Error", f"Failed to create connection: {e}"
130
+ )
131
+ return False
132
+
133
+ def _on_connection_finished(
134
+ self, connection_id: str, success: bool, collections: list, error: str
135
+ ):
136
+ """Handle connection thread completion."""
137
+ self.loading_dialog.hide_loading()
138
+
139
+ # Clean up thread
140
+ thread = self._connection_threads.pop(connection_id, None)
141
+ if thread:
142
+ thread.wait() # Wait for thread to fully finish
143
+ thread.deleteLater()
144
+
145
+ if success:
146
+ # Update state to connected
147
+ self.connection_manager.update_connection_state(
148
+ connection_id, ConnectionState.CONNECTED
149
+ )
150
+
151
+ # Mark connection as opened first (will show in UI)
152
+ self.connection_manager.mark_connection_opened(connection_id)
153
+
154
+ # Then update collections (UI item now exists to receive them)
155
+ self.connection_manager.update_collections(connection_id, collections)
156
+ else:
157
+ # Update state to error
158
+ self.connection_manager.update_connection_state(
159
+ connection_id, ConnectionState.ERROR, error
160
+ )
161
+
162
+ QMessageBox.warning(
163
+ self.parent_widget, "Connection Failed", f"Failed to connect: {error}"
164
+ )
165
+
166
+ # Remove the failed connection
167
+ self.connection_manager.close_connection(connection_id)
168
+
169
+ # Emit signal for UI updates
170
+ self.connection_completed.emit(connection_id, success, collections, error)
171
+
172
+ def cleanup(self):
173
+ """Clean up connection threads on shutdown."""
174
+ for thread in list(self._connection_threads.values()):
175
+ if thread.isRunning():
176
+ thread.quit()
177
+ thread.wait(1000) # Wait up to 1 second