Merge pull request #39130 from BerriAI/litellm_dark_mode_skill_detail

fix(ui): render the skill detail page with theme tokens
This commit is contained in:
yuneng-jiang 2026-09-01 11:52:57 -07:00 committed by GitHub
commit 75f0a22fc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,6 @@
import React, { useState } from "react";
import { ArrowLeft, Check, Copy, Link2 } from "lucide-react";
import { cn } from "@/lib/cva.config";
import { buildMarketplaceSettingsSnippet, formatInstallCommand } from "./helpers";
import { Plugin } from "./types";
@ -50,48 +51,37 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
];
return (
<div style={{ padding: "24px 32px 24px 0" }}>
<div className="py-6 pl-0 pr-8">
{/* Back link */}
<div
onClick={onBack}
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
color: "#5f6368",
cursor: "pointer",
fontSize: 14,
marginBottom: 24,
}}
className="mb-6 inline-flex cursor-pointer items-center gap-1.5 text-sm text-muted-foreground"
>
<ArrowLeft className="size-3" />
<span>Skills</span>
</div>
{/* Header */}
<div style={{ marginBottom: 8 }}>
<h1 style={{ fontSize: 28, fontWeight: 400, color: "#202124", margin: 0, lineHeight: 1.2 }}>{skill.name}</h1>
<div className="mb-2">
<h1 className="m-0 text-[28px] font-normal leading-tight text-foreground">{skill.name}</h1>
{skill.description && (
<p style={{ fontSize: 14, color: "#5f6368", margin: "8px 0 0 0", lineHeight: 1.6 }}>{skill.description}</p>
<p className="mb-0 ml-0 mr-0 mt-2 text-sm leading-relaxed text-muted-foreground">{skill.description}</p>
)}
</div>
{/* Tab bar */}
<div style={{ borderBottom: "1px solid #dadce0", marginBottom: 28, marginTop: 24 }}>
<div style={{ display: "flex", gap: 0 }}>
<div className="mb-7 mt-6 border-b border-border">
<div className="flex">
{tabs.map((tab) => (
<div
key={tab.key}
onClick={() => setActiveTab(tab.key)}
style={{
padding: "12px 20px",
fontSize: 14,
color: activeTab === tab.key ? "#1a73e8" : "#5f6368",
borderBottom: activeTab === tab.key ? "3px solid #1a73e8" : "3px solid transparent",
cursor: "pointer",
fontWeight: activeTab === tab.key ? 500 : 400,
marginBottom: -1,
}}
className={cn(
"-mb-px cursor-pointer border-b-[3px] px-5 py-3 text-sm",
activeTab === tab.key
? "border-info font-medium text-info"
: "border-transparent font-normal text-muted-foreground",
)}
>
{tab.label}
</div>
@ -101,27 +91,23 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
{/* Overview tab */}
{activeTab === "overview" && (
<div style={{ display: "flex", gap: 64 }}>
<div className="flex gap-16">
{/* Left column */}
<div style={{ flex: 1, minWidth: 0 }}>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 4px 0" }}>Skill Details</h2>
<p style={{ fontSize: 13, color: "#5f6368", margin: "0 0 16px 0" }}>Metadata registered with this skill</p>
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 14 }}>
<div className="min-w-0 flex-1">
<h2 className="m-0 mb-1 text-lg font-normal text-foreground">Skill Details</h2>
<p className="m-0 mb-4 text-[13px] text-muted-foreground">Metadata registered with this skill</p>
<table className="w-full border-collapse text-sm">
<thead>
<tr style={{ borderBottom: "1px solid #dadce0" }}>
<th style={{ textAlign: "left", padding: "12px 0", color: "#5f6368", fontWeight: 500, width: 160 }}>
Property
</th>
<th style={{ textAlign: "left", padding: "12px 0", color: "#5f6368", fontWeight: 500 }}>
{skill.name}
</th>
<tr className="border-b border-border">
<th className="w-40 py-3 text-left font-medium text-muted-foreground">Property</th>
<th className="py-3 text-left font-medium text-muted-foreground">{skill.name}</th>
</tr>
</thead>
<tbody>
{detailRows.map((row, i) => (
<tr key={i} style={{ borderBottom: "1px solid #f1f3f4" }}>
<td style={{ padding: "12px 0", color: "#3c4043" }}>{row.property}</td>
<td style={{ padding: "12px 0", color: "#202124" }}>{row.value}</td>
<tr key={i} className="border-b border-border">
<td className="py-3 text-foreground">{row.property}</td>
<td className="py-3 text-foreground">{row.value}</td>
</tr>
))}
</tbody>
@ -129,38 +115,27 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
</div>
{/* Right sidebar */}
<div style={{ width: 240, flexShrink: 0 }}>
<div style={{ marginBottom: 24 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>Status</div>
<div className="w-60 shrink-0">
<div className="mb-6">
<div className="mb-1 text-xs text-muted-foreground">Status</div>
<span
style={{
fontSize: 12,
padding: "3px 10px",
borderRadius: 12,
backgroundColor: skill.enabled ? "#e6f4ea" : "#f1f3f4",
color: skill.enabled ? "#137333" : "#5f6368",
fontWeight: 500,
}}
className={cn(
"rounded-xl px-2.5 py-[3px] text-xs font-medium",
skill.enabled ? "bg-success/10 text-success" : "bg-muted text-muted-foreground",
)}
>
{skill.enabled ? "Public" : "Draft"}
</span>
</div>
{sourceUrl && (
<div style={{ marginBottom: 24 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>Source</div>
<div className="mb-6">
<div className="mb-1 text-xs text-muted-foreground">Source</div>
<a
href={sourceUrl}
target="_blank"
rel="noopener noreferrer"
style={{
fontSize: 13,
color: "#1a73e8",
wordBreak: "break-all",
display: "flex",
alignItems: "center",
gap: 4,
}}
className="flex items-center gap-1 break-all text-[13px] text-info"
>
{sourceUrl.replace("https://", "")}
<Link2 className="size-3 shrink-0" />
@ -169,20 +144,13 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
)}
{skill.keywords && skill.keywords.length > 0 && (
<div style={{ marginBottom: 24 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 8 }}>Tags</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
<div className="mb-6">
<div className="mb-2 text-xs text-muted-foreground">Tags</div>
<div className="flex flex-wrap gap-1.5">
{skill.keywords.map((kw) => (
<span
key={kw}
style={{
fontSize: 12,
padding: "4px 12px",
borderRadius: 16,
border: "1px solid #dadce0",
color: "#3c4043",
backgroundColor: "#fff",
}}
className="rounded-2xl border border-border bg-card px-3 py-1 text-xs text-foreground"
>
{kw}
</span>
@ -192,10 +160,8 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
)}
<div>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>Skill ID</div>
<div style={{ fontSize: 12, fontFamily: "monospace", color: "#3c4043", wordBreak: "break-all" }}>
{skill.id}
</div>
<div className="mb-1 text-xs text-muted-foreground">Skill ID</div>
<div className="break-all font-mono text-xs text-foreground">{skill.id}</div>
</div>
</div>
</div>
@ -203,93 +169,43 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
{/* How to Use tab */}
{activeTab === "usage" && (
<div style={{ maxWidth: 640 }}>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 8px 0" }}>Using this skill</h2>
<p style={{ fontSize: 14, color: "#5f6368", margin: "0 0 24px 0", lineHeight: 1.6 }}>
<div className="max-w-[640px]">
<h2 className="m-0 mb-2 text-lg font-normal text-foreground">Using this skill</h2>
<p className="m-0 mb-6 text-sm leading-relaxed text-muted-foreground">
Once your proxy is set as a marketplace, enable this skill in Claude Code with one command:
</p>
{/* Install command */}
<div
style={{
border: "1px solid #dadce0",
borderRadius: 8,
overflow: "hidden",
marginBottom: 24,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "10px 16px",
backgroundColor: "#f8f9fa",
borderBottom: "1px solid #dadce0",
}}
>
<span style={{ fontSize: 13, color: "#3c4043", fontWeight: 500 }}>Run in Claude Code</span>
<div className="mb-6 overflow-hidden rounded-lg border border-border">
<div className="flex items-center justify-between border-b border-border bg-muted px-4 py-2.5">
<span className="text-[13px] font-medium text-foreground">Run in Claude Code</span>
<button
onClick={() => copyToClipboard(installCommand, "install")}
style={{
display: "flex",
alignItems: "center",
gap: 4,
fontSize: 12,
color: copiedKey === "install" ? "#137333" : "#1a73e8",
background: "none",
border: "none",
cursor: "pointer",
padding: 0,
}}
className={cn(
"flex cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-xs",
copiedKey === "install" ? "text-success" : "text-info",
)}
>
{copiedKey === "install" ? <Check className="size-3" /> : <Copy className="size-3" />}
{copiedKey === "install" ? "Copied" : "Copy"}
</button>
</div>
<pre
style={{
margin: 0,
padding: "14px 16px",
fontSize: 14,
fontFamily: "monospace",
color: "#202124",
backgroundColor: "#fff",
}}
>
{installCommand}
</pre>
<pre className="m-0 bg-card px-4 py-3.5 font-mono text-sm text-foreground">{installCommand}</pre>
</div>
{/* Shown when the marketplace catalog is stale and the plugin isn't found yet */}
<div
style={{
border: "1px solid #fce8b2",
borderRadius: 8,
padding: "12px 16px",
backgroundColor: "#fefce8",
marginBottom: 16,
}}
>
<p style={{ fontSize: 13, color: "#5f6368", lineHeight: 1.6, margin: "0 0 8px 0" }}>
<div className="mb-4 rounded-lg border border-warning/30 bg-warning/10 px-4 py-3">
<p className="m-0 mb-2 text-[13px] leading-relaxed text-muted-foreground">
If you see &quot;Plugin {skill.name} not found in marketplace&quot;, update the catalog first:
</p>
<pre
style={{
margin: 0,
fontSize: 13,
fontFamily: "monospace",
color: "#202124",
backgroundColor: "transparent",
}}
>
<pre className="m-0 bg-transparent font-mono text-[13px] text-foreground">
/plugin marketplace update litellm
</pre>
</div>
<p style={{ fontSize: 13, color: "#5f6368", lineHeight: 1.6, margin: 0 }}>
<p className="m-0 text-[13px] leading-relaxed text-muted-foreground">
Don&apos;t have the marketplace configured yet?{" "}
<span onClick={() => setActiveTab("setup")} style={{ color: "#1a73e8", cursor: "pointer" }}>
<span onClick={() => setActiveTab("setup")} className="cursor-pointer text-info">
See one-time setup
</span>
</p>
@ -298,126 +214,56 @@ const SkillDetail: React.FC<SkillDetailProps> = ({ skill, onBack }) => {
{/* Setup tab (linked from usage) */}
{activeTab === "setup" && (
<div style={{ maxWidth: 640 }}>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 8px 0" }}>
One-time marketplace setup
</h2>
<div className="max-w-[640px]">
<h2 className="m-0 mb-2 text-lg font-normal text-foreground">One-time marketplace setup</h2>
{/* Option 1: single command — fastest path for most users */}
<p style={{ fontSize: 14, color: "#5f6368", margin: "0 0 12px 0", lineHeight: 1.6 }}>
<p className="m-0 mb-3 text-sm leading-relaxed text-muted-foreground">
Run this command in Claude Code to register the marketplace:
</p>
<div
style={{
border: "1px solid #dadce0",
borderRadius: 8,
overflow: "hidden",
marginBottom: 24,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "10px 16px",
backgroundColor: "#f8f9fa",
borderBottom: "1px solid #dadce0",
}}
>
<span style={{ fontSize: 13, color: "#3c4043", fontWeight: 500 }}>Run in Claude Code</span>
<div className="mb-6 overflow-hidden rounded-lg border border-border">
<div className="flex items-center justify-between border-b border-border bg-muted px-4 py-2.5">
<span className="text-[13px] font-medium text-foreground">Run in Claude Code</span>
<button
onClick={() => {
const origin = typeof window !== "undefined" ? window.location.origin : "";
copyToClipboard(`/plugin marketplace add ${origin}/claude-code/marketplace.json`, "marketplace-cmd");
}}
style={{
display: "flex",
alignItems: "center",
gap: 4,
fontSize: 12,
color: copiedKey === "marketplace-cmd" ? "#137333" : "#1a73e8",
background: "none",
border: "none",
cursor: "pointer",
padding: 0,
}}
className={cn(
"flex cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-xs",
copiedKey === "marketplace-cmd" ? "text-success" : "text-info",
)}
>
{copiedKey === "marketplace-cmd" ? <Check className="size-3" /> : <Copy className="size-3" />}
{copiedKey === "marketplace-cmd" ? "Copied" : "Copy"}
</button>
</div>
<pre
style={{
margin: 0,
padding: "14px 16px",
fontSize: 13,
fontFamily: "monospace",
color: "#202124",
backgroundColor: "#fff",
}}
>
<pre className="m-0 bg-card px-4 py-3.5 font-mono text-[13px] text-foreground">
{`/plugin marketplace add ${typeof window !== "undefined" ? window.location.origin : "<proxy-url>"}/claude-code/marketplace.json`}
</pre>
</div>
{/* Option 2: settings.json for persistent config or managed deployments.
extraKnownMarketplaces requires source to be a nested object, not a flat string. */}
<p style={{ fontSize: 14, color: "#5f6368", margin: "0 0 12px 0", lineHeight: 1.6 }}>
Or add this to{" "}
<code style={{ fontSize: 13, backgroundColor: "#f1f3f4", padding: "1px 6px", borderRadius: 4 }}>
~/.claude/settings.json
</code>{" "}
<p className="m-0 mb-3 text-sm leading-relaxed text-muted-foreground">
Or add this to <code className="rounded bg-muted px-1.5 py-px text-[13px]">~/.claude/settings.json</code>{" "}
for a persistent configuration:
</p>
<div
style={{
border: "1px solid #dadce0",
borderRadius: 8,
overflow: "hidden",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "10px 16px",
backgroundColor: "#f8f9fa",
borderBottom: "1px solid #dadce0",
}}
>
<span style={{ fontSize: 13, color: "#3c4043", fontWeight: 500 }}>~/.claude/settings.json</span>
<div className="overflow-hidden rounded-lg border border-border">
<div className="flex items-center justify-between border-b border-border bg-muted px-4 py-2.5">
<span className="text-[13px] font-medium text-foreground">~/.claude/settings.json</span>
<button
onClick={() => copyToClipboard(settingsSnippet, "settings")}
style={{
display: "flex",
alignItems: "center",
gap: 4,
fontSize: 12,
color: copiedKey === "settings" ? "#137333" : "#1a73e8",
background: "none",
border: "none",
cursor: "pointer",
padding: 0,
}}
className={cn(
"flex cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-xs",
copiedKey === "settings" ? "text-success" : "text-info",
)}
>
{copiedKey === "settings" ? <Check className="size-3" /> : <Copy className="size-3" />}
{copiedKey === "settings" ? "Copied" : "Copy"}
</button>
</div>
<pre
style={{
margin: 0,
padding: "14px 16px",
fontSize: 13,
fontFamily: "monospace",
color: "#202124",
backgroundColor: "#fff",
}}
>
{settingsSnippet}
</pre>
<pre className="m-0 bg-card px-4 py-3.5 font-mono text-[13px] text-foreground">{settingsSnippet}</pre>
</div>
</div>
)}