fix(ui): restore official terminology

This commit is contained in:
Patrick Erichsen
2026-07-23 10:59:05 -07:00
parent 594a7be992
commit eb3050fdcc
24 changed files with 66 additions and 50 deletions
+17 -1
View File
@@ -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();
+1 -1
View File
@@ -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(
+3 -3
View File
@@ -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 () => {
+2 -2
View File
@@ -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();
+1 -1
View File
@@ -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];
+1 -1
View File
@@ -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];
+5 -5
View File
@@ -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>
+4 -4
View File
@@ -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();
});
+2 -2
View File
@@ -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", () => {
+3 -2
View File
@@ -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();
});
-1
View File
@@ -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>
+4 -4
View File
@@ -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();
});
+4 -4
View File
@@ -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>
+1 -1
View File
@@ -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),
+3 -3
View File
@@ -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();
});
+2 -2
View File
@@ -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}
+2 -2
View File
@@ -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();
});
+3 -3
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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">
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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) => (
+1 -1
View File
@@ -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" },
+1 -1
View File
@@ -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>