Add collapsible README viewer to tool details panel and owner override for publish

- Add collapsible README section to tools page detail panel with lazy loading
- README loads from disk only when user clicks the toggle bar
- Section collapses to a single header bar, expands to show content
- Add --owner flag to registry publish command for admin use
- Simplify dependency gathering in publish to use unified code path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-02-01 01:26:56 -04:00
parent 94e33c0763
commit f2cb9c0057
3 changed files with 94 additions and 16 deletions

View File

@ -187,6 +187,7 @@ def main():
p_reg_publish.add_argument("path", nargs="?", help="Path to tool directory (default: current dir)")
p_reg_publish.add_argument("--dry-run", action="store_true", help="Validate without publishing")
p_reg_publish.add_argument("-f", "--force", action="store_true", help="Skip confirmation prompts")
p_reg_publish.add_argument("--owner", default="", help="Owner override (admin only, e.g. 'official')")
p_reg_publish.set_defaults(func=cmd_registry)
# registry my-tools

View File

@ -456,21 +456,20 @@ def _cmd_registry_publish(args):
my_owner = None
tool = load_tool(name)
if tool:
dep_result = gather_local_unpublished_deps([name], client, my_owner)
else:
# Tool isn't installed locally; derive deps from config.yaml
temp_tool = Tool.from_dict(data)
dep_names = []
for dep in temp_tool.dependencies:
if '/' not in dep:
dep_names.append(dep)
for step in temp_tool.steps:
if isinstance(step, ToolStep) and '/' not in step.tool:
dep_names.append(step.tool)
dep_names = sorted(set(dep_names))
if dep_names:
dep_result = gather_local_unpublished_deps(dep_names, client, my_owner)
if not tool:
# Tool isn't installed locally; derive from config.yaml
tool = Tool.from_dict(data)
dep_names = []
for dep in tool.dependencies:
if '/' not in dep:
dep_names.append(dep)
for step in tool.steps:
if isinstance(step, ToolStep) and '/' not in step.tool:
dep_names.append(step.tool)
dep_names = sorted(set(dep_names) - {name})
if dep_names:
dep_result = gather_local_unpublished_deps(dep_names, client, my_owner)
except Exception as e:
print(f"Warning: Could not check dependencies: {e}", file=sys.stderr)
dep_result = None
@ -560,7 +559,8 @@ def _cmd_registry_publish(args):
try:
client = get_client()
result = client.publish_tool(config_yaml, readme, defaults)
owner = getattr(args, "owner", "")
result = client.publish_tool(config_yaml, readme, defaults, owner=owner)
pr_url = result.get("pr_url", "")
status = result.get("status", "")

View File

@ -365,6 +365,7 @@ class ToolsPage(QWidget):
self._issue_worker = None
self._my_slug: Optional[str] = None # Cached current user slug
self._my_slug_fetched = False
self._readme_loaded_for: Optional[str] = None # Track which tool's README is loaded
self._setup_ui()
self.refresh()
@ -452,6 +453,37 @@ class ToolsPage(QWidget):
rating_bar_layout.addWidget(self.btn_report_issue)
self.rating_bar.setVisible(False)
# Collapsible README section
self.readme_container = QWidget()
readme_container_layout = QVBoxLayout(self.readme_container)
readme_container_layout.setContentsMargins(0, 8, 0, 0)
readme_container_layout.setSpacing(0)
self.readme_toggle = QPushButton("▶ README")
self.readme_toggle.setStyleSheet(
"QPushButton { text-align: left; padding: 6px 10px; "
"background: #edf2f7; border: 1px solid #e2e8f0; border-radius: 4px; "
"color: #4a5568; font-weight: 600; font-size: 12px; }"
"QPushButton:hover { background: #e2e8f0; }"
)
self.readme_toggle.setCursor(Qt.PointingHandCursor)
self.readme_toggle.clicked.connect(self._on_readme_toggled)
readme_container_layout.addWidget(self.readme_toggle)
self.readme_text = QTextEdit()
self.readme_text.setReadOnly(True)
self.readme_text.setMinimumHeight(200)
self.readme_text.setStyleSheet(
"QTextEdit { font-family: monospace; font-size: 12px; "
"border: 1px solid #e2e8f0; border-top: none; border-radius: 0 0 4px 4px; }"
)
self.readme_text.setVisible(False)
readme_container_layout.addWidget(self.readme_text)
self.readme_container.setVisible(False)
info_layout.addWidget(self.readme_container)
info_layout.addWidget(self.rating_bar)
right_layout.addWidget(info_box, 1)
@ -527,6 +559,7 @@ class ToolsPage(QWidget):
self._current_tool = None
self.info_text.clear()
self.rating_bar.setVisible(False)
self.readme_container.setVisible(False)
self._has_pending_tools = False # Reset, will be set during tree building
tools = list_tools()
@ -724,6 +757,7 @@ class ToolsPage(QWidget):
self._current_tool = None
self.info_text.clear()
self.rating_bar.setVisible(False)
self.readme_container.setVisible(False)
self._update_buttons()
return
@ -871,6 +905,49 @@ class ToolsPage(QWidget):
# Update rating bar below the detail text
self._update_rating_bar(qname)
# Reset README section (collapsed, visible, lazy-loaded on expand)
self._current_tool = tool
self._readme_loaded_for = None
self.readme_text.clear()
self.readme_text.setVisible(False)
self.readme_toggle.setText("▶ README")
self.readme_container.setVisible(tool.path is not None)
def _on_readme_toggled(self):
"""Toggle README section visibility and lazy-load content on first expand."""
expanding = not self.readme_text.isVisible()
self.readme_text.setVisible(expanding)
self.readme_toggle.setText("▼ README" if expanding else "▶ README")
if not expanding:
return
tool = self._current_tool
if not tool or not tool.path:
return
# Avoid reloading if already loaded for this tool
if self._readme_loaded_for == tool.name:
return
readme_path = tool.path.parent / "README.md"
if readme_path.exists():
try:
content = readme_path.read_text()
escaped = content.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
self.readme_text.setHtml(
f"<pre style='white-space: pre-wrap; font-family: monospace; "
f"font-size: 12px; color: #2d3748;'>{escaped}</pre>"
)
except Exception as e:
self.readme_text.setPlainText(f"Error reading README: {e}")
else:
self.readme_text.setHtml(
"<p style='color: #718096; font-style: italic;'>No README.md found for this tool.</p>"
)
self._readme_loaded_for = tool.name
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, self._my_slug)