From b1c73aba3f37a3c6715f023abb511ae628d8f5ea Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 30 Jan 2026 02:27:54 -0400 Subject: [PATCH] Move rating display and Rate button to bottom of tool details panel Instead of the Rate button sitting in the action bar with Create/Edit/Delete, the average rating, review count, and Rate/Edit Rating button now appear as a bar at the bottom of the tool detail section where they contextually belong. Co-Authored-By: Claude Opus 4.5 --- src/cmdforge/gui/pages/tools_page.py | 104 +++++++++++++++------------ 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/src/cmdforge/gui/pages/tools_page.py b/src/cmdforge/gui/pages/tools_page.py index 4d9738e..6f21912 100644 --- a/src/cmdforge/gui/pages/tools_page.py +++ b/src/cmdforge/gui/pages/tools_page.py @@ -363,6 +363,29 @@ class ToolsPage(QWidget): self.info_text.setPlaceholderText("Select a tool to view details") info_layout.addWidget(self.info_text) + # Rating bar at bottom of details panel + self.rating_bar = QWidget() + rating_bar_layout = QHBoxLayout(self.rating_bar) + rating_bar_layout.setContentsMargins(4, 4, 4, 4) + rating_bar_layout.setSpacing(12) + + self.rating_label = QLabel("") + self.rating_label.setTextFormat(Qt.RichText) + self.rating_label.setStyleSheet("color: #4a5568; font-size: 12px;") + rating_bar_layout.addWidget(self.rating_label) + + rating_bar_layout.addStretch() + + self.btn_rate = QPushButton("Rate Tool") + self.btn_rate.setObjectName("secondary") + self.btn_rate.clicked.connect(self._rate_tool) + self.btn_rate.setEnabled(False) + self.btn_rate.setToolTip("Local tools can't be rated") + rating_bar_layout.addWidget(self.btn_rate) + + self.rating_bar.setVisible(False) + info_layout.addWidget(self.rating_bar) + right_layout.addWidget(info_box, 1) splitter.addWidget(right) @@ -401,13 +424,6 @@ class ToolsPage(QWidget): self.btn_delete.setEnabled(False) btn_layout.addWidget(self.btn_delete) - self.btn_rate = QPushButton("Rate Tool") - self.btn_rate.setObjectName("secondary") - self.btn_rate.clicked.connect(self._rate_tool) - self.btn_rate.setEnabled(False) - self.btn_rate.setToolTip("Local tools can't be rated") - btn_layout.addWidget(self.btn_rate) - btn_layout.addStretch() # Connect/Publish button @@ -442,6 +458,7 @@ class ToolsPage(QWidget): self.tool_tree.clear() self._current_tool = None self.info_text.clear() + self.rating_bar.setVisible(False) self._has_pending_tools = False # Reset, will be set during tree building tools = list_tools() @@ -628,6 +645,7 @@ class ToolsPage(QWidget): if not items: self._current_tool = None self.info_text.clear() + self.rating_bar.setVisible(False) self._update_buttons() return @@ -636,6 +654,7 @@ class ToolsPage(QWidget): if not tool_name: self._current_tool = None self.info_text.clear() + self.rating_bar.setVisible(False) self._update_buttons() return @@ -737,43 +756,6 @@ class ToolsPage(QWidget): f"Reason:
{feedback_escaped}" ) - # Rating section (for registry-sourced tools) - registry_info = get_tool_registry_info(qname) - if registry_info: - cached = self._rating_cache.get(qname) - if cached: - rating_data = cached.get("rating") - my_review = cached.get("my_review") - if rating_data: - avg = rating_data.get("average_rating", 0) - count = rating_data.get("rating_count", 0) - if count > 0: - filled = round(avg) - stars_html = "".join( - "★" if i < filled else "☆" for i in range(5) - ) - lines.append( - f"

" - f"{stars_html} " - f"{avg:.1f}/5 from {count} review{'s' if count != 1 else ''}

" - ) - else: - lines.append( - "

" - "No ratings yet

" - ) - if my_review: - my_stars = "★" * my_review["rating"] + "☆" * (5 - my_review["rating"]) - lines.append( - f"

" - f"Your rating: {my_stars}

" - ) - elif registry_info: - lines.append( - "

" - "Loading ratings...

" - ) - # Source info if tool.source: source_type = tool.source.type @@ -817,6 +799,40 @@ class ToolsPage(QWidget): self.info_text.setHtml("\n".join(lines)) + # Update rating bar below the detail text + self._update_rating_bar(qname) + + def _update_rating_bar(self, qname: str): + """Update the rating bar widget at the bottom of the detail panel.""" + registry_info = get_tool_registry_info(qname) + if not registry_info: + self.rating_bar.setVisible(False) + return + + self.rating_bar.setVisible(True) + cached = self._rating_cache.get(qname) + + if cached: + rating_data = cached.get("rating") + my_review = cached.get("my_review") + parts = [] + if rating_data: + avg = rating_data.get("average_rating", 0) + count = rating_data.get("rating_count", 0) + if count > 0: + filled = round(avg) + stars = "".join("★" if i < filled else "☆" for i in range(5)) + parts.append(f"{stars}" + f" {avg:.1f}/5 from {count} review{'s' if count != 1 else ''}") + else: + parts.append("No ratings yet") + if my_review: + my_stars = "★" * my_review["rating"] + "☆" * (5 - my_review["rating"]) + parts.append(f"Your rating: {my_stars}") + self.rating_label.setText(" · ".join(parts) if parts else "") + else: + self.rating_label.setText("Loading ratings...") + def _has_settings(self, tool: Tool) -> bool: """Check if a tool has configurable settings.""" if not tool or not tool.path: