feat: refine skills.sh listing UI (#3373)

* feat: refine skills.sh listing UI

* test: fix skills.sh missing metric fixture
This commit is contained in:
Patrick Erichsen
2026-08-01 21:43:09 -07:00
committed by GitHub
parent 059cf01765
commit 632850f5a8
6 changed files with 53 additions and 58 deletions
@@ -155,10 +155,42 @@ describe("HomeListingSection", () => {
render(<HomeListingSection initialListing={initialTrending([external])} />);
expect(screen.getByText("@doany-skills")).toBeTruthy();
expect(screen.queryByText("skills.sh")).toBeNull();
const sourceBadge = screen.getByText("skills.sh");
expect(sourceBadge.getAttribute("title")).toBe("Synced from skills.sh");
expect(screen.getByLabelText("Downloads").textContent).toContain("12.3k");
});
it("keeps skills.sh provenance visible when the upstream install count is unavailable", () => {
const external = {
...makeTrending("unmetered", "Unmetered", 0, 0, 0),
id: "skills-sh:example/skills/unmetered",
source: "skills-sh" as const,
canonicalUrl: "/skills-sh/example/skills/unmetered",
publisher: null,
sourceIdentity: {
id: "example/skills/unmetered",
owner: "example",
repo: "skills",
host: null,
lifetimeInstalls: null,
},
metrics: {
trending24hDownloads: null,
trending24hInstalls: null,
trending24hBookmarks: null,
lifetimeInstalls: null,
lifetimeInstallsPeriod: "lifetime" as const,
updatedAt: 1,
},
};
render(<HomeListingSection initialListing={initialTrending([external])} />);
const sourceBadge = screen.getByText("skills.sh");
expect(sourceBadge.getAttribute("title")).toBe("Synced from skills.sh");
expect(screen.getByLabelText("Downloads").textContent).toBe("skills.sh");
});
it("hides unavailable Trending and falls back to the Featured feed", async () => {
render(<HomeListingSection initialListing={initialTrending([], false, "unavailable")} />);
+1
View File
@@ -382,6 +382,7 @@ describe("restored UI design contract", () => {
expect(homeSource).not.toContain("Trending Now");
expect(cssRule(css, ".home-v2-listing-toolbar")).toContain("display: flex");
expect(cssRule(css, ".home-v2-listing-kind")).toContain("margin-left: auto");
expect(cssRule(css, ".home-v2-listing-row::before")).toContain("border-radius: 0");
});
it("requires the restored footer columns and mobile section toggles", () => {
+11 -5
View File
@@ -22,6 +22,7 @@ import { presentationTitle } from "../lib/presentationTitle";
import { PUBLIC_CATALOG_NAME_PREVIEW_LENGTH, truncateText } from "../lib/truncateText";
import { OfficialBadge } from "./OfficialBadge";
import { BrowseResultsSkeleton } from "./skeletons/BrowseResultsSkeleton";
import { Badge } from "./ui/badge";
const SKILL_LISTING_TABS: Array<{ id: ListingTab; label: string }> = [
{ id: "trending", label: "Trending" },
@@ -142,11 +143,16 @@ function HomeListingSkillRow({ entry }: { entry: SkillPageEntry }) {
{truncateText(item.summary || "Agent-ready skill pack.", 80)}
</p>
</div>
{isSkillsSh && typeof upstreamInstalls === "number" ? (
<div className="home-v2-listing-row-stats" aria-label="Downloads">
<span title={`${upstreamInstalls.toLocaleString()} skills.sh installs`}>
{formatCompactStat(upstreamInstalls)}
</span>
{isSkillsSh ? (
<div className="home-v2-listing-row-stats is-skills-sh" aria-label="Downloads">
{typeof upstreamInstalls === "number" ? (
<span title={`${upstreamInstalls.toLocaleString()} skills.sh installs`}>
{formatCompactStat(upstreamInstalls)}
</span>
) : null}
<Badge variant="compact" size="sm" title="Synced from skills.sh">
skills.sh
</Badge>
</div>
) : typeof item.metrics.trending24hDownloads === "number" ? (
<div className="home-v2-listing-row-stats" aria-label="Downloads">
+2 -16
View File
@@ -59,7 +59,7 @@ describe("SkillsShCatalogDetailPage", () => {
expect(container.querySelector(".skills-sh-detail-source-badge")).toBeNull();
});
it("shows the GitHub owner, repository, and a preselected GitHub Skill Sync claim", () => {
it("shows the GitHub owner and repository without a public claim action", () => {
render(<SkillsShCatalogDetailPage entry={makeEntry()} />);
expect(screen.getByText("openclaw skills install")).toBeTruthy();
@@ -77,21 +77,7 @@ describe("SkillsShCatalogDetailPage", () => {
expect(repository.classList.contains("plugin-external-link")).toBe(true);
expect(repository.querySelector("svg")).toBeTruthy();
expect(screen.queryByRole("link", { name: "View on skills.sh" })).toBeNull();
const claimUrl = new URL(
screen.getByRole("link", { name: "Claim this skill" }).getAttribute("href") ?? "",
"https://clawhub.test",
);
expect(claimUrl.pathname).toBe("/settings");
expect(Object.fromEntries(claimUrl.searchParams)).toEqual({
view: "githubSources",
ownerHandle: "openclaw",
repo: "openclaw/openclaw",
sourceRepo: "openclaw/openclaw",
sourceExternalId: "patrick-erichsen/skills/html",
sourcePath: "skills/html",
sourceCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
sourceContentHash: "a".repeat(64),
});
expect(screen.queryByRole("link", { name: "Claim this skill" })).toBeNull();
});
it("renders only stored bounded content and no file explorer", () => {
+1 -25
View File
@@ -1,5 +1,4 @@
import { Link } from "@tanstack/react-router";
import { BadgeCheck, ShieldAlert } from "lucide-react";
import { ShieldAlert } from "lucide-react";
import { getSkillCategoriesForSkill } from "../lib/categories";
import { formatCompactStat } from "../lib/numberFormat";
import {
@@ -15,7 +14,6 @@ import { stripFrontmatter } from "./skillDetailUtils";
import { SkillCommandLineCard } from "./SkillInstallSurface";
import { Alert, AlertDescription } from "./ui/alert";
import { Badge } from "./ui/badge";
import { Button } from "./ui/button";
import { UserBadge } from "./UserBadge";
const CHECK_PRESENTATION = {
@@ -221,28 +219,6 @@ function SkillsShSidebar({ entry }: { entry: SkillsShCatalogDetail }) {
))}
</div>
</section>
<div className="skills-sh-detail-links">
{entry.githubPath && entry.githubCommit && entry.githubContentHash ? (
<Button asChild variant="outline" size="sm">
<Link
to="/settings"
search={{
view: "githubSources",
ownerHandle: entry.canonicalGitHubRepo.split("/")[0],
repo: entry.canonicalGitHubRepo,
sourceRepo: entry.canonicalGitHubRepo,
sourceExternalId: entry.externalId,
sourcePath: entry.githubPath,
sourceCommit: entry.githubCommit,
sourceContentHash: entry.githubContentHash,
}}
>
<BadgeCheck size={15} aria-hidden="true" /> Claim this skill
</Link>
</Button>
) : null}
</div>
</div>
);
}
+5 -11
View File
@@ -7397,16 +7397,6 @@ a.skills-sh-security-audit-row:focus-visible {
background: var(--hover-bg);
}
.skills-sh-detail-links {
display: grid;
gap: 8px;
}
.skills-sh-detail-links .btn {
justify-content: flex-start;
width: 100%;
}
.skills-sh-content-note {
margin: 0;
padding-bottom: 14px;
@@ -23161,7 +23151,7 @@ a.search-empty-action {
content: "";
position: absolute;
inset: 0 0 2px;
border-radius: 10px;
border-radius: 0;
background: transparent;
pointer-events: none;
}
@@ -23331,6 +23321,10 @@ a.search-empty-action {
white-space: nowrap;
}
.home-v2-listing-row-stats.is-skills-sh {
gap: 7px;
}
.home-v2-listing-row-stats span {
display: inline-flex;
align-items: center;