Add fork display, version selector, and admin cleanup features
Registry Features: - Fork tracking with forked_from/forked_version metadata - Forked tools show "Forked from" notice on detail page - Original tools display "Forks" section listing all forks - Fork count in tool stats - API: GET /api/v1/tools/<owner>/<name>/forks GUI Improvements: - Version selector dropdown for registry installs - Fetch available versions via GET /api/v1/tools/<owner>/<name>/versions - "Latest" option plus all available versions listed Admin Features: - POST /api/v1/admin/cleanup/rejected endpoint - Maintenance section in admin dashboard - "Dry Run" and "Run Cleanup" buttons for rejected version cleanup - Configurable grace period (default 7 days) Documentation: - Updated CHANGELOG.md with all recent changes Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
@@ -57,9 +57,9 @@ class SearchWorker(QThread):
|
||||
self.error.emit(str(e))
|
||||
|
||||
|
||||
class InstallWorker(QThread):
|
||||
"""Background worker for tool installation."""
|
||||
finished = Signal(str)
|
||||
class VersionsWorker(QThread):
|
||||
"""Background worker to fetch available versions."""
|
||||
finished = Signal(list) # List of version strings
|
||||
error = Signal(str)
|
||||
|
||||
def __init__(self, owner: str, name: str):
|
||||
@@ -70,8 +70,29 @@ class InstallWorker(QThread):
|
||||
def run(self):
|
||||
try:
|
||||
client = RegistryClient()
|
||||
client.install_tool(self.owner, self.name)
|
||||
self.finished.emit(f"{self.owner}/{self.name}")
|
||||
versions = client.get_tool_versions(self.owner, self.name)
|
||||
self.finished.emit(versions)
|
||||
except Exception as e:
|
||||
self.error.emit(str(e))
|
||||
|
||||
|
||||
class InstallWorker(QThread):
|
||||
"""Background worker for tool installation."""
|
||||
finished = Signal(str)
|
||||
error = Signal(str)
|
||||
|
||||
def __init__(self, owner: str, name: str, version: str = None):
|
||||
super().__init__()
|
||||
self.owner = owner
|
||||
self.name = name
|
||||
self.version = version
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
client = RegistryClient()
|
||||
client.install_tool(self.owner, self.name, version=self.version)
|
||||
version_str = f"@{self.version}" if self.version else ""
|
||||
self.finished.emit(f"{self.owner}/{self.name}{version_str}")
|
||||
except Exception as e:
|
||||
self.error.emit(str(e))
|
||||
|
||||
@@ -84,7 +105,9 @@ class RegistryPage(QWidget):
|
||||
self.main_window = main_window
|
||||
self._search_worker = None
|
||||
self._install_worker = None
|
||||
self._versions_worker = None
|
||||
self._selected_tool = None
|
||||
self._available_versions = []
|
||||
self._current_page = 1
|
||||
self._total_pages = 1
|
||||
self._current_tags = []
|
||||
@@ -228,6 +251,23 @@ class RegistryPage(QWidget):
|
||||
|
||||
right_layout.addWidget(details_box, 1)
|
||||
|
||||
# Version selector row
|
||||
version_row = QHBoxLayout()
|
||||
version_row.setSpacing(8)
|
||||
|
||||
version_label = QLabel("Version:")
|
||||
version_label.setStyleSheet("color: #4a5568;")
|
||||
version_row.addWidget(version_label)
|
||||
|
||||
self.version_combo = QComboBox()
|
||||
self.version_combo.setMinimumWidth(120)
|
||||
self.version_combo.addItem("Latest", None)
|
||||
self.version_combo.setEnabled(False)
|
||||
version_row.addWidget(self.version_combo)
|
||||
|
||||
version_row.addStretch()
|
||||
right_layout.addLayout(version_row)
|
||||
|
||||
# Action buttons
|
||||
actions = QHBoxLayout()
|
||||
|
||||
@@ -405,6 +445,9 @@ class RegistryPage(QWidget):
|
||||
self.details_text.clear()
|
||||
self.btn_install.setEnabled(False)
|
||||
self.btn_update.hide()
|
||||
self.version_combo.clear()
|
||||
self.version_combo.addItem("Latest", None)
|
||||
self.version_combo.setEnabled(False)
|
||||
return
|
||||
|
||||
row = items[0].row()
|
||||
@@ -413,11 +456,47 @@ class RegistryPage(QWidget):
|
||||
self._selected_tool = tool
|
||||
self._show_tool_details(tool)
|
||||
|
||||
# Reset and fetch versions
|
||||
self.version_combo.clear()
|
||||
self.version_combo.addItem("Loading...", None)
|
||||
self.version_combo.setEnabled(False)
|
||||
self._fetch_versions(tool.get("owner", ""), tool.get("name", ""))
|
||||
|
||||
# Check if installed / update available
|
||||
tool_name = tool.get("name", "")
|
||||
self._update_install_buttons()
|
||||
|
||||
def _fetch_versions(self, owner: str, name: str):
|
||||
"""Fetch available versions for a tool."""
|
||||
self._versions_worker = VersionsWorker(owner, name)
|
||||
self._versions_worker.finished.connect(self._on_versions_fetched)
|
||||
self._versions_worker.error.connect(self._on_versions_error)
|
||||
self._versions_worker.start()
|
||||
|
||||
def _on_versions_fetched(self, versions: list):
|
||||
"""Handle versions fetch completion."""
|
||||
self._available_versions = versions
|
||||
self.version_combo.clear()
|
||||
self.version_combo.addItem("Latest", None)
|
||||
for version in versions:
|
||||
self.version_combo.addItem(f"v{version}", version)
|
||||
self.version_combo.setEnabled(len(versions) > 1)
|
||||
|
||||
def _on_versions_error(self, error: str):
|
||||
"""Handle versions fetch error."""
|
||||
self._available_versions = []
|
||||
self.version_combo.clear()
|
||||
self.version_combo.addItem("Latest", None)
|
||||
self.version_combo.setEnabled(False)
|
||||
|
||||
def _update_install_buttons(self):
|
||||
"""Update install/update buttons based on selection."""
|
||||
if not self._selected_tool:
|
||||
return
|
||||
|
||||
tool_name = self._selected_tool.get("name", "")
|
||||
if tool_name in self._installed_tools:
|
||||
installed_version = self._installed_tools[tool_name]
|
||||
registry_version = tool.get("version", "1.0.0")
|
||||
registry_version = self._selected_tool.get("version", "1.0.0")
|
||||
if installed_version != registry_version:
|
||||
self.btn_install.hide()
|
||||
self.btn_update.show()
|
||||
@@ -546,12 +625,16 @@ class RegistryPage(QWidget):
|
||||
|
||||
owner = self._selected_tool.get("owner", "")
|
||||
name = self._selected_tool.get("name", "")
|
||||
version = self.version_combo.currentData() # None for "Latest"
|
||||
|
||||
self.btn_install.setEnabled(False)
|
||||
self.btn_update.setEnabled(False)
|
||||
self.status_label.setText(f"Installing {owner}/{name}...")
|
||||
self.version_combo.setEnabled(False)
|
||||
|
||||
self._install_worker = InstallWorker(owner, name)
|
||||
version_str = f"@{version}" if version else ""
|
||||
self.status_label.setText(f"Installing {owner}/{name}{version_str}...")
|
||||
|
||||
self._install_worker = InstallWorker(owner, name, version)
|
||||
self._install_worker.finished.connect(self._on_install_complete)
|
||||
self._install_worker.error.connect(self._on_install_error)
|
||||
self._install_worker.start()
|
||||
|
||||
Reference in New Issue
Block a user