this repo has no description
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(verified): account-wide verification + brand checkmark seal

Reframes the icon-access gate as project verification. Verifying a
project does two things: drops the new starburst checkmark seal next
to the project name on listing cards and the detail-page hero, and
unlocks SVG icon uploads for the developer API. Copy throughout the
form, modal, admin overview, and admin/icon-access page is updated to
match — "SVG icon verification" is just "Verification" everywhere.

- new components/VerifiedBadge.tsx renders the seal as inline SVG
with currentColor; brand-blue (#254a9e) by default via CSS
- ProfileHero (22px) and ProfileCard (16px) show the seal when
profile.iconAccessStatus === "granted"
- i18n: rename gate / modal / admin strings to drop SVG framing,
add badges.verifiedTooltip, switch admin actions to Verify / Verified
- /api/registry/profile error detail rephrased

Made-with: Cursor

+101 -15
+21
assets/styles.css
··· 2005 2005 color: #fff; 2006 2006 } 2007 2007 2008 + /* Verified-project seal: shown next to the project name when an 2009 + admin has cleared the project. Distinct from `.profile-badge--*`, 2010 + which are curator-controlled featured rail labels. */ 2011 + .profile-verified-badge { 2012 + display: inline-flex; 2013 + align-items: center; 2014 + justify-content: center; 2015 + color: #254a9e; 2016 + flex-shrink: 0; 2017 + line-height: 1; 2018 + } 2019 + .profile-verified-badge--inline { 2020 + vertical-align: -0.15em; 2021 + } 2022 + .profile-verified-badge svg { 2023 + display: block; 2024 + } 2025 + .profile-verified-badge:hover { 2026 + color: #1f4f96; 2027 + } 2028 + 2008 2029 .explore-empty { 2009 2030 text-align: center; 2010 2031 padding: 3rem 1rem;
+51
components/VerifiedBadge.tsx
··· 1 + import { useT } from "../i18n/mod.ts"; 2 + 3 + interface Props { 4 + /** Pixel size of the rendered badge. Defaults to the inline-with-text size. */ 5 + size?: number; 6 + /** Adds a small block-margin so the badge sits flush with adjacent headings. */ 7 + inline?: boolean; 8 + /** Override accessible label; defaults to t.badges.verifiedTooltip. */ 9 + label?: string; 10 + } 11 + 12 + /** 13 + * Verified-project seal. Rendered next to the project name anywhere 14 + * a project is surfaced (listing cards, detail hero, etc.) when the 15 + * project has cleared admin verification (`iconAccessStatus === "granted"`). 16 + * 17 + * The shape is the brand starburst seal with an inset checkmark; fill 18 + * uses `currentColor` so the colour can be tuned per surface from CSS 19 + * (default is the primary brand blue via `.profile-verified-badge`). 20 + */ 21 + export default function VerifiedBadge( 22 + { size = 18, inline = true, label }: Props, 23 + ) { 24 + const t = useT(); 25 + const accessibleLabel = label ?? t.badges.verifiedTooltip; 26 + return ( 27 + <span 28 + class={`profile-verified-badge${ 29 + inline ? " profile-verified-badge--inline" : "" 30 + }`} 31 + title={accessibleLabel} 32 + role="img" 33 + aria-label={accessibleLabel} 34 + > 35 + <svg 36 + width={size} 37 + height={size} 38 + viewBox="0 0 100 100" 39 + fill="none" 40 + xmlns="http://www.w3.org/2000/svg" 41 + aria-hidden="true" 42 + focusable="false" 43 + > 44 + <path 45 + d="M100 52.3438C100 55.8398 99.1602 59.082 97.4805 62.0508C95.8008 65.0195 93.5547 67.3438 90.7227 68.9648C90.8008 69.4922 90.8398 70.3125 90.8398 71.4258C90.8398 76.7188 89.0625 81.2109 85.5469 84.9219C82.0117 88.6523 77.7539 90.5078 72.7734 90.5078C70.5469 90.5078 68.418 90.0977 66.4062 89.2773C64.8438 92.4805 62.5977 95.0586 59.6484 97.0312C56.7187 99.0234 53.4961 100 50 100C46.4258 100 43.1836 99.043 40.293 97.0898C37.3828 95.1563 35.1562 92.5586 33.5938 89.2773C31.582 90.0977 29.4727 90.5078 27.2266 90.5078C22.2461 90.5078 17.9687 88.6523 14.3945 84.9219C10.8203 81.2109 9.04297 76.6992 9.04297 71.4258C9.04297 70.8398 9.12109 70.0195 9.25781 68.9648C6.42578 67.3242 4.17969 65.0195 2.5 62.0508C0.839844 59.082 0 55.8398 0 52.3438C0 48.6328 0.9375 45.2148 2.79297 42.1289C4.64844 39.043 7.14844 36.7578 10.2734 35.2734C9.45312 33.0469 9.04297 30.8008 9.04297 28.5742C9.04297 23.3008 10.8203 18.7891 14.3945 15.0781C17.9687 11.3672 22.2461 9.49219 27.2266 9.49219C29.4531 9.49219 31.582 9.90234 33.5938 10.7227C35.1562 7.51953 37.4023 4.94141 40.3516 2.96875C43.2813 0.996094 46.5039 0 50 0C53.4961 0 56.7187 0.996094 59.6484 2.94922C62.5781 4.92188 64.8438 7.5 66.4062 10.7031C68.418 9.88281 70.5273 9.47266 72.7734 9.47266C77.7539 9.47266 82.0117 11.3281 85.5469 15.0586C89.082 18.7891 90.8398 23.2812 90.8398 28.5547C90.8398 31.0156 90.4687 33.2422 89.7266 35.2539C92.8516 36.7383 95.3516 39.0234 97.207 42.1094C99.0625 45.2148 100 48.6328 100 52.3438ZM47.8711 67.4023L68.5156 36.4844C69.043 35.6641 69.1992 34.7656 69.0234 33.8086C68.8281 32.8516 68.3398 32.0898 67.5195 31.582C66.6992 31.0547 65.8008 30.8789 64.8438 31.0156C63.8672 31.1719 63.0859 31.6406 62.5 32.4609L44.3164 59.8047L35.9375 51.4453C35.1953 50.7031 34.3359 50.3516 33.3789 50.3906C32.4023 50.4297 31.5625 50.7812 30.8203 51.4453C30.1563 52.1094 29.8242 52.9492 29.8242 53.9648C29.8242 54.9609 30.1563 55.8008 30.8203 56.4844L42.3242 67.9883L42.8906 68.4375C43.5547 68.8867 44.2383 69.1016 44.9023 69.1016C46.2109 69.082 47.207 68.5352 47.8711 67.4023Z" 46 + fill="currentColor" 47 + /> 48 + </svg> 49 + </span> 50 + ); 51 + }
+6
components/explore/ProfileCard.tsx
··· 1 1 import type { ProfileRow } from "../../lib/registry.ts"; 2 2 import { useT } from "../../i18n/mod.ts"; 3 + import VerifiedBadge from "../VerifiedBadge.tsx"; 3 4 4 5 interface Props { 5 6 profile: ProfileRow; ··· 40 41 <div class="profile-card-body"> 41 42 <div class="profile-card-title-row"> 42 43 <h3 class="profile-card-name">{profile.name}</h3> 44 + {profile.iconAccessStatus === "granted" && ( 45 + <VerifiedBadge 46 + size={16} 47 + /> 48 + )} 43 49 {featured?.badges?.includes("official") && ( 44 50 <span class="profile-badge profile-badge--official"> 45 51 {t.badges.official}
+6
components/explore/ProfileHero.tsx
··· 1 1 import type { ProfileRow } from "../../lib/registry.ts"; 2 2 import { useT } from "../../i18n/mod.ts"; 3 + import VerifiedBadge from "../VerifiedBadge.tsx"; 3 4 4 5 interface Props { 5 6 profile: ProfileRow; ··· 58 59 <div class="profile-hero-body"> 59 60 <div class="profile-hero-name-row"> 60 61 <h1 class="profile-hero-name">{profile.name}</h1> 62 + {profile.iconAccessStatus === "granted" && ( 63 + <VerifiedBadge 64 + size={22} 65 + /> 66 + )} 61 67 {featured?.badges?.includes("official") && ( 62 68 <span class="profile-badge profile-badge--official"> 63 69 {tBadges.official}
+16 -14
i18n/messages/en.tsx
··· 442 442 badges: { 443 443 verified: "Verified", 444 444 official: "Official", 445 + /** Tooltip / aria label for the verified-seal icon shown next to 446 + * the project name once admin verification is granted. */ 447 + verifiedTooltip: "Verified project", 445 448 }, 446 449 447 450 /** ··· 610 613 tooLarge: "Icon must be 200KB or smaller.", 611 614 gate: { 612 615 /** Gate state when the project hasn't requested verification yet. */ 613 - lockedTitle: "SVG Upload requires verification", 616 + lockedTitle: "Verification required", 614 617 lockedBody: 615 - "Per-project verification keeps malicious SVGs out of the developer API. Submit a request and an admin will review your project.", 618 + "Verified projects get a checkmark on their listing and unlock SVG icon uploads for the developer API. Submit a request and an admin will review your project.", 616 619 requestButton: "Request Verification", 617 620 /** Disabled-button text shown before the user has published their profile. */ 618 621 requestDisabledHint: ··· 629 632 : `An admin denied your verification request. To appeal, email ${appealEmail}.`, 630 633 /** Gate state after admin grant — uploader unlocked. */ 631 634 grantedHint: 632 - "Your project is verified — SVG uploads are unlocked. Files are still sanitised on upload.", 635 + "Your project is verified — a checkmark appears on your listing and SVG uploads are unlocked. Files are still sanitised on upload.", 633 636 }, 634 637 /** Modal that collects a contact email for the verification request. */ 635 638 requestModal: { 636 - title: "Request SVG icon verification", 639 + title: "Request verification", 637 640 body: 638 - "An admin will review your project and reply by email. We only use this address for this verification thread.", 641 + "An admin will review your project and reply by email. Verified projects get a checkmark on their listing and can upload an SVG icon for the developer API.", 639 642 emailLabel: "Contact email", 640 643 emailPlaceholder: "you@example.com", 641 644 submit: "Submit request", ··· 660 663 errorPrefix: "Error", 661 664 overview: { 662 665 headline: "Admin", 663 - subhead: 664 - "Verify projects for SVG uploads, triage reports, and curate the featured rail.", 665 - iconAccessTitle: "Icon access requests", 666 + subhead: "Verify projects, triage reports, and curate the featured rail.", 667 + iconAccessTitle: "Verification requests", 666 668 iconAccessBody: 667 - "Projects requesting permission to upload an SVG icon for the developer API.", 669 + "Projects asking to be verified — grants a checkmark on their listing and unlocks SVG icon uploads for the developer API.", 668 670 reportsTitle: "Open reports", 669 671 reportsBody: "User-submitted reports against profiles in Explore.", 670 672 featuredTitle: "Featured", ··· 680 682 rejected: "Rejected", 681 683 }, 682 684 iconAccess: { 683 - headline: "Icon access requests", 685 + headline: "Verification requests", 684 686 subhead: 685 - "Projects asking for permission to upload an SVG icon. Granting unlocks /api/registry/icon/:did and the developer API's `iconUrl`. Per-icon sanitisation still runs server-side. Denying (or revoking) hides any existing icon immediately.", 687 + "Projects asking to be verified. Granting puts a checkmark next to the project name on its listing and detail page, and unlocks /api/registry/icon/:did + the developer API's `iconUrl`. Per-icon sanitisation still runs server-side. Denying (or revoking) drops the checkmark and hides any existing icon immediately.", 686 688 pendingHeading: "Pending requests", 687 689 grantedHeading: "Currently verified", 688 690 emptyPending: "No requests in the queue.", 689 691 emptyGranted: "No projects are verified yet.", 690 - grant: "Grant", 692 + grant: "Verify", 691 693 deny: "Deny", 692 694 revoke: "Revoke", 693 695 denyPrompt: 694 696 "Optional: tell the project owner why you're denying / revoking. Press OK with the field empty to deny without a reason.", 695 - markedGranted: "Granted", 697 + markedGranted: "Verified", 696 698 markedDenied: "Denied", 697 699 requestedAtLabel: "Requested", 698 - grantedAtLabel: "Granted", 700 + grantedAtLabel: "Verified", 699 701 emailLabel: "Contact email", 700 702 viewProfile: "View profile", 701 703 },
+1 -1
routes/api/registry/profile.ts
··· 205 205 JSON.stringify({ 206 206 error: "icon_access_required", 207 207 detail: 208 - "SVG icon uploads require admin verification. Request access from your profile page.", 208 + "This project hasn't been verified yet. SVG icon uploads unlock once an admin verifies the project — request verification from your profile page.", 209 209 }), 210 210 { 211 211 status: 403,