mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix(ui): restore official terminology
This commit is contained in:
@@ -176,6 +176,22 @@ describe("creators route", () => {
|
||||
expect(screen.getByText("No publishers found")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("labels highlighted publishers as popular creators", async () => {
|
||||
loaderDataMock.mockReturnValue({
|
||||
page: [{ _id: "publishers:one" }],
|
||||
counts: { all: 1, organizations: 0, individuals: 1 },
|
||||
continueCursor: "",
|
||||
isDone: true,
|
||||
});
|
||||
const route = await loadRoute();
|
||||
const Component = route.__config.component as ComponentType;
|
||||
|
||||
render(<Component />);
|
||||
|
||||
expect(screen.getByRole("heading", { name: "Popular creators" })).toBeTruthy();
|
||||
expect(screen.queryByText("Popular publishers")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not present the bounded publisher result count as a global total", async () => {
|
||||
loaderDataMock.mockReturnValue({
|
||||
page: [],
|
||||
@@ -192,7 +208,7 @@ describe("creators route", () => {
|
||||
expect(screen.getByRole("heading", { name: "Creators" })).toBeTruthy();
|
||||
expect(screen.queryByText("17")).toBeNull();
|
||||
expect(screen.getByRole("radio", { name: "All" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Verified" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Official" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Organizations" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Users" })).toBeTruthy();
|
||||
expect(screen.queryByText("Builders")).toBeNull();
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("HomeListingSection", () => {
|
||||
"Trending",
|
||||
]);
|
||||
expect(screen.queryByRole("tab", { name: "New" })).toBeNull();
|
||||
expect(screen.queryByRole("tab", { name: "Verified" })).toBeNull();
|
||||
expect(screen.queryByRole("tab", { name: "Official" })).toBeNull();
|
||||
expect(screen.getByText("Demo Plugin")).toBeTruthy();
|
||||
expect(document.querySelector(".home-v2-listing-grid")).toBeTruthy();
|
||||
expect(document.querySelector(".marketplace-icon-image")?.getAttribute("src")).toBe(
|
||||
|
||||
@@ -1117,7 +1117,7 @@ describe("plugin detail route", () => {
|
||||
expect(packageCrumb?.textContent).toBe("firecrawl-plugin");
|
||||
});
|
||||
|
||||
it("labels official packages as Verified", async () => {
|
||||
it("labels official packages as Official", async () => {
|
||||
loaderDataMock = {
|
||||
...loaderDataMock,
|
||||
detail: {
|
||||
@@ -1134,10 +1134,10 @@ describe("plugin detail route", () => {
|
||||
|
||||
const { container } = render(<Component />);
|
||||
|
||||
expect(screen.getAllByLabelText("Verified").length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByLabelText("Official").length).toBeGreaterThan(0);
|
||||
expect(container.querySelector(".skill-hero-creator .official-badge-icon-only")).toBeTruthy();
|
||||
expect(container.querySelector(".skill-hero-title-row .official-tag")).toBeNull();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders plugin activity skeletons while graphs load", async () => {
|
||||
|
||||
@@ -1584,7 +1584,7 @@ describe("plugins route", () => {
|
||||
render(<Component />);
|
||||
|
||||
expect(screen.getByRole("radio", { name: "All" }).getAttribute("aria-checked")).toBe("true");
|
||||
expect(screen.getByRole("radio", { name: "Verified" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Official" })).toBeTruthy();
|
||||
expect(screen.getByRole("radio", { name: "Updated" })).toBeTruthy();
|
||||
expect(screen.queryByRole("radio", { name: "Relevance" })).toBeNull();
|
||||
});
|
||||
@@ -1730,7 +1730,7 @@ describe("plugins route", () => {
|
||||
const sortOptions = Array.from(
|
||||
screen.getByRole("radiogroup", { name: "Sort order" }).querySelectorAll('[role="radio"]'),
|
||||
).map((option) => option.textContent);
|
||||
expect(sortOptions).toEqual(["All", "Trending", "Verified", "Updated"]);
|
||||
expect(sortOptions).toEqual(["All", "Trending", "Official", "Updated"]);
|
||||
expect(screen.queryByRole("radio", { name: "Most downloaded" })).toBeNull();
|
||||
expect(screen.queryByRole("radio", { name: "Newest" })).toBeNull();
|
||||
expect(screen.queryByRole("radio", { name: "Name" })).toBeNull();
|
||||
|
||||
@@ -22,7 +22,7 @@ const FOOTER_EASTER_ASCII = [
|
||||
" hooks runners slash-commands skill.md templates scanners review-bots",
|
||||
"openclaw ecosystem crabbox clickclack crawler packs gateway plugins",
|
||||
"---- downloads installs stars lineage ownership docs package integrity",
|
||||
" safe browse paths verified gateways publisher handles org trust",
|
||||
" safe browse paths official gateways publisher handles org trust",
|
||||
];
|
||||
const FOOTER_EASTER_ASCII_FIELD = Array.from({ length: 44 }, (_, row) => {
|
||||
const a = FOOTER_EASTER_ASCII[row % FOOTER_EASTER_ASCII.length];
|
||||
|
||||
@@ -12,7 +12,7 @@ const BYOS_ASCII = [
|
||||
" hooks runners slash-commands skill.md templates scanners review-bots",
|
||||
"openclaw ecosystem crabbox clickclack crawler packs gateway plugins",
|
||||
"---- downloads installs stars lineage ownership docs package integrity",
|
||||
" safe browse paths verified gateways publisher handles org trust",
|
||||
" safe browse paths official gateways publisher handles org trust",
|
||||
];
|
||||
const BYOS_ASCII_FIELD = Array.from({ length: 56 }, (_, row) => {
|
||||
const a = BYOS_ASCII[row % BYOS_ASCII.length];
|
||||
|
||||
@@ -10,10 +10,10 @@ export function OfficialTag({ className }: { className?: string }) {
|
||||
? `official-tag rounded-[var(--oc-radius-control)] ${className}`
|
||||
: "official-tag rounded-[var(--oc-radius-control)]"
|
||||
}
|
||||
aria-label="Verified"
|
||||
aria-label="Official"
|
||||
>
|
||||
<BadgeCheck size={15} aria-hidden="true" className="official-badge-icon" />
|
||||
Verified
|
||||
Official
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -29,14 +29,14 @@ export function OfficialBadge({ className, iconOnly = false, size = 12 }: Offici
|
||||
const iconClassName = className
|
||||
? `official-badge-icon-only ${className}`
|
||||
: "official-badge-icon-only";
|
||||
return <BadgeCheck size={size} className={iconClassName} aria-label="Verified" />;
|
||||
return <BadgeCheck size={size} className={iconClassName} aria-label="Official" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={className ? `official-badge ${className}` : "official-badge"}
|
||||
aria-label="Verified"
|
||||
title="Verified"
|
||||
aria-label="Official"
|
||||
title="Official"
|
||||
>
|
||||
<BadgeCheck size={size} aria-hidden="true" />
|
||||
</span>
|
||||
|
||||
@@ -14,16 +14,16 @@ describe("PluginListItem", () => {
|
||||
it("renders official list plugins with the compact official mark", () => {
|
||||
render(<PluginListItem item={makePlugin()} />);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders official plugin cards with the compact official mark", () => {
|
||||
render(<PluginListItem item={makePlugin()} variant="card" />);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ describe("PublishedItemCard", () => {
|
||||
|
||||
it("renders the compact official mark for official published rows", () => {
|
||||
render(<PublishedItemCard item={{ ...baseSkill, icon: null, isOfficial: true }} />);
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not add source-backed chrome to GitHub-backed skill rows", () => {
|
||||
|
||||
@@ -15,8 +15,9 @@ describe("PublisherListItem", () => {
|
||||
it("renders official publishers with the compact official mark", () => {
|
||||
const { container } = render(<PublisherListItem publisher={makePublisher()} />);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
expect(screen.queryByText("Org")).toBeNull();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ export function PublisherListItem({ publisher, variant = "list" }: PublisherList
|
||||
<span className="publisher-card-title-row">
|
||||
<span className="publisher-card-name">{publisher.displayName}</span>
|
||||
{publisher.official ? <OfficialBadge /> : null}
|
||||
{publisher.kind === "org" ? <span className="publisher-card-kind">Org</span> : null}
|
||||
</span>
|
||||
<span className="publisher-card-handle">@{handle}</span>
|
||||
</span>
|
||||
|
||||
@@ -16,14 +16,14 @@ describe("SkillCard", () => {
|
||||
const { container } = render(
|
||||
<SkillCard
|
||||
skill={makeSkill()}
|
||||
badge="Verified"
|
||||
badge="Official"
|
||||
summaryFallback="Fallback summary"
|
||||
meta={<span>meta</span>}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("SkillCard", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ export function SkillCard({
|
||||
const ownerSegment = encodeURIComponent(String(skill.ownerUserId));
|
||||
const link = href ?? `/${ownerSegment}/${skill.slug}`;
|
||||
const badges = Array.isArray(badge) ? badge : badge ? [badge] : [];
|
||||
const isOfficial = badges.includes("Verified") || owner?.official === true;
|
||||
const nonOfficialBadges = badges.filter((label) => label !== "Verified");
|
||||
const isOfficial = badges.includes("Official") || owner?.official === true;
|
||||
const nonOfficialBadges = badges.filter((label) => label !== "Official");
|
||||
const visibleBadges = ownerHandle
|
||||
? nonOfficialBadges
|
||||
: isOfficial
|
||||
? ["Verified", ...nonOfficialBadges]
|
||||
? ["Official", ...nonOfficialBadges]
|
||||
: badges;
|
||||
const primaryCategory = getSkillCategoryForSkill(skill);
|
||||
const hasSecondaryTags =
|
||||
@@ -88,7 +88,7 @@ export function SkillCard({
|
||||
<span className="skill-card-tag-separator" aria-hidden="true" />
|
||||
) : null}
|
||||
{visibleBadges.map((label) =>
|
||||
label === "Verified" ? (
|
||||
label === "Official" ? (
|
||||
<OfficialBadge key={label} />
|
||||
) : (
|
||||
<Badge key={label}>{label}</Badge>
|
||||
|
||||
@@ -179,7 +179,7 @@ export function SkillHeader({
|
||||
const hasOwnerActions = Boolean(newVersionHref) || Boolean(settingsHref);
|
||||
const showReportAction = !canManage || isStaff;
|
||||
const badges = getSkillBadges(skill);
|
||||
const titleBadges = badges.filter((badge) => badge !== "Verified");
|
||||
const titleBadges = badges.filter((badge) => badge !== "Official");
|
||||
const heroCreatorPublisher = useHeroCreatorPublisher({
|
||||
owner,
|
||||
skillOfficial: isSkillOfficial(skill),
|
||||
|
||||
@@ -27,8 +27,8 @@ describe("SkillListItem", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.queryByText("Verified")).toBeNull();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(screen.queryByText("Official")).toBeNull();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("SkillListItem", () => {
|
||||
<SkillListItem skill={makeSkill()} owner={makePublisher({ official: true })} />,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export function SkillListItem({
|
||||
const href =
|
||||
hrefOverride ?? `/${encodeURIComponent(ownerSegment)}/${encodeURIComponent(skill.slug)}`;
|
||||
const badges = getSkillBadges(skill);
|
||||
const isOfficial = badges.includes("Verified") || owner?.official === true;
|
||||
const isOfficial = badges.includes("Official") || owner?.official === true;
|
||||
const categories = getSkillCategoriesForSkill(skill);
|
||||
const categoryLabel = categories
|
||||
.slice(0, 3)
|
||||
@@ -49,7 +49,7 @@ export function SkillListItem({
|
||||
</span>
|
||||
{isOfficial ? <OfficialBadge /> : null}
|
||||
{badges
|
||||
.filter((badge) => badge !== "Verified")
|
||||
.filter((badge) => badge !== "Official")
|
||||
.map((badge) => (
|
||||
<Badge key={badge} variant="compact">
|
||||
{badge}
|
||||
|
||||
@@ -134,10 +134,10 @@ describe("UserBadge", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("shows a compact Verified badge for official publishers", () => {
|
||||
it("shows a compact Official badge for official publishers", () => {
|
||||
const { container } = renderBadge(orgPublisher);
|
||||
|
||||
expect(screen.getByLabelText("Verified")).toBeTruthy();
|
||||
expect(screen.getByLabelText("Official")).toBeTruthy();
|
||||
expect(container.querySelector(".official-badge")).toBeTruthy();
|
||||
expect(container.querySelector(".official-tag")).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -67,12 +67,12 @@ describe("badges", () => {
|
||||
).toEqual(["Deprecated"]);
|
||||
});
|
||||
|
||||
it("returns Verified when official is set", () => {
|
||||
it("returns Official when official is set", () => {
|
||||
expect(
|
||||
getSkillBadges({
|
||||
badges: { official: { byUserId: "user1" as never, at: 123 } },
|
||||
}),
|
||||
).toEqual(["Verified"]);
|
||||
).toEqual(["Official"]);
|
||||
});
|
||||
|
||||
it("does not surface Highlighted as a trust badge", () => {
|
||||
@@ -92,7 +92,7 @@ describe("badges", () => {
|
||||
highlighted: { byUserId: "user1" as never, at: 123 },
|
||||
},
|
||||
}),
|
||||
).toEqual(["Deprecated", "Verified"]);
|
||||
).toEqual(["Deprecated", "Official"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ type SkillBadgeMap = Partial<Record<BadgeKind, { byUserId: Id<"users">; at: numb
|
||||
|
||||
type SkillLike = { badges?: SkillBadgeMap | null };
|
||||
|
||||
type BadgeLabel = "Deprecated" | "Verified";
|
||||
type BadgeLabel = "Deprecated" | "Official";
|
||||
|
||||
export function isSkillHighlighted(skill: SkillLike) {
|
||||
return Boolean(skill.badges?.highlighted);
|
||||
@@ -23,6 +23,6 @@ export function isSkillDeprecated(skill: SkillLike) {
|
||||
export function getSkillBadges(skill: SkillLike): BadgeLabel[] {
|
||||
const badges: BadgeLabel[] = [];
|
||||
if (isSkillDeprecated(skill)) badges.push("Deprecated");
|
||||
if (isSkillOfficial(skill)) badges.push("Verified");
|
||||
if (isSkillOfficial(skill)) badges.push("Official");
|
||||
return badges;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export function PluginsPage({
|
||||
</div>
|
||||
<div className="management-tags">
|
||||
<Badge>{plugin.channel}</Badge>
|
||||
{plugin.isOfficial ? <Badge variant="official">Verified</Badge> : null}
|
||||
{plugin.isOfficial ? <Badge variant="official">Official</Badge> : null}
|
||||
{plugin.runtimeId ? <Badge>{plugin.runtimeId}</Badge> : null}
|
||||
</div>
|
||||
<div className="management-sublist">
|
||||
|
||||
@@ -407,7 +407,7 @@ export function SkillsPage({
|
||||
type="button"
|
||||
onClick={() => onSetOfficialBadge(skill._id, !isOfficial)}
|
||||
>
|
||||
{isOfficial ? "Remove verified" : "Mark verified"}
|
||||
{isOfficial ? "Remove official" : "Mark official"}
|
||||
</Button>
|
||||
<Button
|
||||
className="management-action-btn"
|
||||
|
||||
@@ -50,7 +50,7 @@ const PUBLISHER_KIND_OPTIONS = [
|
||||
{ value: undefined, label: "All" },
|
||||
{
|
||||
value: "official",
|
||||
label: "Verified",
|
||||
label: "Official",
|
||||
icon: <BadgeCheck size={14} strokeWidth={2.25} aria-hidden="true" />,
|
||||
},
|
||||
{ value: "orgs", label: "Organizations", mobileLabel: "Orgs" },
|
||||
@@ -344,7 +344,7 @@ function PublishersIndex() {
|
||||
{highlightedPublishers.length > 0 ? (
|
||||
<section className="publisher-highlights" aria-labelledby="publisher-highlights-title">
|
||||
<div className="publisher-section-heading">
|
||||
<h2 id="publisher-highlights-title">Popular publishers</h2>
|
||||
<h2 id="publisher-highlights-title">Popular creators</h2>
|
||||
</div>
|
||||
<div className="publisher-highlight-grid">
|
||||
{highlightedPublishers.map((publisher) => (
|
||||
|
||||
@@ -63,7 +63,7 @@ const PLUGIN_BROWSE_TABS = [
|
||||
{ value: "trending", label: "Trending" },
|
||||
{
|
||||
value: "official",
|
||||
label: "Verified",
|
||||
label: "Official",
|
||||
icon: <BadgeCheck size={14} strokeWidth={2.25} aria-hidden="true" />,
|
||||
},
|
||||
{ value: "updated", label: "Updated" },
|
||||
|
||||
@@ -1662,7 +1662,7 @@ export function Settings() {
|
||||
/>
|
||||
) : (
|
||||
<p className="rounded-[var(--radius-sm)] border border-[color:var(--line)] bg-[color:var(--surface-muted)]/25 p-3 text-sm text-[color:var(--ink-soft)]">
|
||||
You need a verified publisher profile before adding GitHub skill sync.
|
||||
You need an official publisher profile before adding GitHub skill sync.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user