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 <noreply@anthropic.com>
This commit is contained in:
rob 2026-01-30 02:27:54 -04:00
parent 14b3f3d856
commit b1c73aba3f
1 changed files with 60 additions and 44 deletions

View File

@ -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"<strong>Reason:</strong><br>{feedback_escaped}</div>"
)
# 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"<p style='color: #d69e2e; font-size: 14px; margin-bottom: 4px;'>"
f"{stars_html} <span style='color: #4a5568; font-size: 12px;'>"
f"{avg:.1f}/5 from {count} review{'s' if count != 1 else ''}</span></p>"
)
else:
lines.append(
"<p style='color: #718096; font-size: 12px; margin-bottom: 4px;'>"
"No ratings yet</p>"
)
if my_review:
my_stars = "" * my_review["rating"] + "" * (5 - my_review["rating"])
lines.append(
f"<p style='color: #805ad5; font-size: 12px; margin-bottom: 8px;'>"
f"Your rating: {my_stars}</p>"
)
elif registry_info:
lines.append(
"<p style='color: #718096; font-size: 12px; margin-bottom: 4px;'>"
"Loading ratings...</p>"
)
# 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"<span style='color: #d69e2e; font-size: 14px;'>{stars}</span>"
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"<span style='color: #805ad5;'>Your rating: {my_stars}</span>")
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: