mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
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:
@@ -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")} />);
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user