mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 18:31:34 +01:00
Apply Skills Store detail-page polish from PAP-10882 UX review
Three small but board-feedback-grounded fixes on the new full-page skill detail (PAP-10846): - Hide the trust-level (TrustChip) and compatibility (CompatChip) pills from the detail header. Round-1 #8 said trust stays internal in v1; the "Includes scripts" pill was the explicit user-facing surface to remove. Round-2 #1.f also dropped compatibility from the stats redistribution. Source pin moved to a github-icon + tooltip next to the categories. - Add an installs count next to the Star control in the header (Star, N installs, Fork, ...) — round-2 #1.a's redistribution target for the removed stats bar. - Reorder the right rail to Attach to agent → Related skills → Sharing (round-2 #2). Sharing is still rendered on this surface because the control is editable; the read-only scope pill was already absent. Plus one small discovery-grid fallback: - Bundled SKILL.md files with frontmatter-only bodies stored their extracted description as ">" (raw blockquote glyph), which showed up literally on cards. cardDescriptionText() strips leading markdown punctuation and falls through to "No description yet." when nothing meaningful remains. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
2f93dc99a6
commit
a151e4618c
1 file changed
+70
-43
@@ -530,6 +530,16 @@ function formatBytes(bytes: number) {
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
}
|
||||
|
||||
// Some bundled skills ship with a frontmatter-only SKILL.md whose extracted
|
||||
// description is just punctuation (e.g. ">"). Strip leading markdown syntax and
|
||||
// fall back to a neutral placeholder so cards don't render bare glyphs.
|
||||
function cardDescriptionText(raw: string | null | undefined): string {
|
||||
const cleaned = (raw ?? "")
|
||||
.replace(/^[\s>#*_\-`>]+/, "")
|
||||
.trim();
|
||||
return cleaned.length >= 3 ? cleaned : "No description yet.";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Skills Store discovery grid (PAP-10879)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -835,7 +845,7 @@ function SkillCard({ card, onOpen }: { card: DiscoveryCard; onOpen: (card: Disco
|
||||
) : null}
|
||||
|
||||
<p className="mt-2 line-clamp-2 text-xs text-muted-foreground">
|
||||
{card.description || "No description yet."}
|
||||
{cardDescriptionText(card.description)}
|
||||
</p>
|
||||
|
||||
<div className="mt-auto pt-3">
|
||||
@@ -2936,15 +2946,19 @@ function SkillDetailPage({
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-wrap items-center gap-2">
|
||||
<TrustChip level={detail.trustLevel} />
|
||||
<CompatChip compatibility={detail.compatibility} />
|
||||
{detail.categories.slice(0, 4).map((category) => (
|
||||
<SkillCategoryChip key={category} label={category} />
|
||||
))}
|
||||
{detail.sourceType === "github" ? (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{currentPin ? `Source ${currentPin}` : "Untracked source"}
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<Github className="h-3 w-3" aria-hidden="true" />
|
||||
{currentPin ?? "untracked"}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{currentPin ? `Source pin ${currentPin}` : "No source pin tracked"}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2954,6 +2968,16 @@ function SkillDetailPage({
|
||||
{detail.starredByCurrentActor ? "Starred" : "Star"}
|
||||
<span className="ml-1 text-xs text-muted-foreground">{detail.starCount}</span>
|
||||
</Button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground">
|
||||
<Download className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
<span>{detail.installCount}</span>
|
||||
<span className="hidden sm:inline">{detail.installCount === 1 ? "install" : "installs"}</span>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Times this skill has been installed or forked.</TooltipContent>
|
||||
</Tooltip>
|
||||
<Button variant="ghost" size="sm" onClick={onFork}>
|
||||
<GitFork className="mr-1.5 h-3.5 w-3.5" />
|
||||
Fork
|
||||
@@ -3001,43 +3025,6 @@ function SkillDetailPage({
|
||||
</main>
|
||||
|
||||
<aside className="min-w-0 space-y-6 border-t border-border pt-4 xl:border-l xl:border-t-0 xl:pl-5 xl:pt-0">
|
||||
<section>
|
||||
<div className="mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">Sharing</div>
|
||||
<div className="space-y-2">
|
||||
{(["company", "private"] as const).map((scope) => (
|
||||
<button
|
||||
key={scope}
|
||||
type="button"
|
||||
onClick={() => onUpdateSharingScope(scope)}
|
||||
disabled={updateSharingPending || detail.sharingScope === scope}
|
||||
className={cn(
|
||||
"flex w-full items-start gap-2 rounded-md border px-3 py-2 text-left text-sm transition-colors",
|
||||
detail.sharingScope === scope ? "border-foreground/50 bg-accent/40" : "border-border hover:bg-accent/30",
|
||||
)}
|
||||
>
|
||||
{scope === "company" ? <Users className="mt-0.5 h-3.5 w-3.5" /> : <Lock className="mt-0.5 h-3.5 w-3.5" />}
|
||||
<span className="min-w-0">
|
||||
<span className="block font-medium">{scope === "company" ? "Company" : "Private"}</span>
|
||||
<span className="mt-0.5 block text-xs text-muted-foreground">
|
||||
{scope === "company" ? "Visible inside this company." : "Only visible in your library."}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
className="flex w-full items-start gap-2 rounded-md border border-dashed border-border px-3 py-2 text-left text-sm text-muted-foreground"
|
||||
>
|
||||
<Globe className="mt-0.5 h-3.5 w-3.5" />
|
||||
<span>
|
||||
<span className="block font-medium">Public link</span>
|
||||
<span className="mt-0.5 block text-xs">Coming later.</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">Attach to agent</div>
|
||||
<div className="space-y-2">
|
||||
@@ -3077,6 +3064,46 @@ function SkillDetailPage({
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Sharing scope only surfaces when the viewer can edit it (round 2 #3).
|
||||
Sits below Attach/Related so the primary action lives at the top
|
||||
of the rail (round 2 #2). */}
|
||||
<section>
|
||||
<div className="mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">Sharing</div>
|
||||
<div className="space-y-2">
|
||||
{(["company", "private"] as const).map((scope) => (
|
||||
<button
|
||||
key={scope}
|
||||
type="button"
|
||||
onClick={() => onUpdateSharingScope(scope)}
|
||||
disabled={updateSharingPending || detail.sharingScope === scope}
|
||||
className={cn(
|
||||
"flex w-full items-start gap-2 rounded-md border px-3 py-2 text-left text-sm transition-colors",
|
||||
detail.sharingScope === scope ? "border-foreground/50 bg-accent/40" : "border-border hover:bg-accent/30",
|
||||
)}
|
||||
>
|
||||
{scope === "company" ? <Users className="mt-0.5 h-3.5 w-3.5" /> : <Lock className="mt-0.5 h-3.5 w-3.5" />}
|
||||
<span className="min-w-0">
|
||||
<span className="block font-medium">{scope === "company" ? "Company" : "Private"}</span>
|
||||
<span className="mt-0.5 block text-xs text-muted-foreground">
|
||||
{scope === "company" ? "Visible inside this company." : "Only visible in your library."}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
className="flex w-full items-start gap-2 rounded-md border border-dashed border-border px-3 py-2 text-left text-sm text-muted-foreground"
|
||||
>
|
||||
<Globe className="mt-0.5 h-3.5 w-3.5" />
|
||||
<span>
|
||||
<span className="block font-medium">Public link</span>
|
||||
<span className="mt-0.5 block text-xs">Coming later.</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user