feat(ui): AIHub/marketplace_table_columns to shadcn

- @tremor Button/Badge/Text + antd Tooltip + @ant-design CopyOutlined
  \u2192 shadcn Button/Badge/Tooltip + lucide Copy.
- Tremor color name \u2192 Tailwind class mapping for category badges
  (blue, emerald, amber, red, purple, indigo, gray).

Gates: TS \u2713 | Build \u2713.

Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-04-23 13:36:27 +00:00
parent 7e50afed51
commit 10868011ac
No known key found for this signature in database
2 changed files with 208 additions and 162 deletions

View file

@ -68,6 +68,7 @@
"src/components/agent_management/AgentSelector.tsx",
"src/components/CostTrackingSettings/how_it_works.tsx",
"src/components/Settings/AdminSettings/UISettings/PageVisibilitySettings.tsx",
"src/components/AIHub/marketplace_table_columns.tsx",
"src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx",
"src/components/view_logs/ToolsSection/FormattedToolView.tsx",
"src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx",

View file

@ -1,7 +1,14 @@
import { ColumnDef } from "@tanstack/react-table";
import { Button, Badge, Text } from "@tremor/react";
import { Tooltip } from "antd";
import { CopyOutlined } from "@ant-design/icons";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { Copy } from "lucide-react";
import { MarketplacePluginEntry } from "@/components/claude_code_plugins/types";
import {
formatInstallCommand,
@ -9,170 +16,208 @@ import {
getSourceDisplayText,
} from "@/components/claude_code_plugins/helpers";
// Map tremor palette names to categorical Tailwind classes
const categoryColorClass: Record<string, string> = {
blue: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300",
green:
"bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300",
emerald:
"bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300",
amber:
"bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-300",
yellow:
"bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-300",
red: "bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-300",
purple:
"bg-purple-100 text-purple-700 dark:bg-purple-950 dark:text-purple-300",
indigo:
"bg-indigo-100 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300",
gray: "bg-muted text-muted-foreground",
};
export const getMarketplaceTableColumns = (
copyToClipboard: (text: string) => void,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
publicPage: boolean = false,
): ColumnDef<MarketplacePluginEntry>[] => {
const allColumns: ColumnDef<MarketplacePluginEntry>[] = [
{
header: "Plugin Name",
accessorKey: "name",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
const installCommand = formatInstallCommand(plugin);
): ColumnDef<MarketplacePluginEntry>[] => [
{
header: "Plugin Name",
accessorKey: "name",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
const installCommand = formatInstallCommand(plugin);
return (
<div className="space-y-1">
<div className="flex items-center space-x-2">
<Text className="font-medium text-sm">{plugin.name}</Text>
<Tooltip title="Copy install command">
<CopyOutlined
onClick={() => copyToClipboard(installCommand)}
className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs"
/>
</Tooltip>
</div>
{/* Show description on mobile */}
<div className="md:hidden">
<Text className="text-xs text-gray-600">
{plugin.description || "No description"}
</Text>
</div>
</div>
);
},
},
{
header: "Description",
accessorKey: "description",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
return (
<Text className="text-xs line-clamp-2">
{plugin.description || "-"}
</Text>
);
},
meta: {
className: "hidden md:table-cell",
},
},
{
header: "Version",
accessorKey: "version",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
return plugin.version ? (
<Badge color="blue" size="sm">
v{plugin.version}
</Badge>
) : (
<Text className="text-xs text-gray-400">-</Text>
);
},
meta: {
className: "hidden lg:table-cell",
},
},
{
header: "Category",
accessorKey: "category",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
const badgeColor = getCategoryBadgeColor(plugin.category);
return plugin.category ? (
<Badge color={badgeColor} size="sm">
{plugin.category}
</Badge>
) : (
<Badge color="gray" size="sm">
Uncategorized
</Badge>
);
},
meta: {
className: "hidden lg:table-cell",
},
},
{
header: "Source",
accessorKey: "source",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
const sourceText = getSourceDisplayText(plugin.source);
return <Text className="text-xs text-gray-600">{sourceText}</Text>;
},
meta: {
className: "hidden xl:table-cell",
},
},
{
header: "Keywords",
accessorKey: "keywords",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
const keywords = plugin.keywords?.slice(0, 3) || [];
const remaining = (plugin.keywords?.length || 0) - 3;
return (
<div className="flex flex-wrap gap-1">
{keywords.map((keyword, index) => (
<Badge key={index} color="gray" size="xs">
{keyword}
</Badge>
))}
{remaining > 0 && (
<Badge color="gray" size="xs">
+{remaining}
</Badge>
)}
</div>
);
},
meta: {
className: "hidden xl:table-cell",
},
},
{
header: "Install Command",
id: "install_command",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
const installCommand = formatInstallCommand(plugin);
return (
return (
<div className="space-y-1">
<div className="flex items-center space-x-2">
<code className="text-xs bg-gray-100 px-2 py-1 rounded font-mono truncate max-w-[200px]">
{installCommand}
</code>
<Tooltip title="Copy command">
<Button
size="xs"
variant="secondary"
icon={CopyOutlined}
onClick={() => copyToClipboard(installCommand)}
/>
</Tooltip>
<span className="font-medium text-sm">{plugin.name}</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => copyToClipboard(installCommand)}
className="cursor-pointer text-muted-foreground hover:text-primary"
aria-label="Copy install command"
>
<Copy className="h-3 w-3" />
</button>
</TooltipTrigger>
<TooltipContent>Copy install command</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
},
<div className="md:hidden">
<p className="text-xs text-muted-foreground">
{plugin.description || "No description"}
</p>
</div>
</div>
);
},
];
},
{
header: "Description",
accessorKey: "description",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
return (
<p className="text-xs line-clamp-2">{plugin.description || "-"}</p>
);
},
meta: {
className: "hidden md:table-cell",
},
},
{
header: "Version",
accessorKey: "version",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
return plugin.version ? (
<Badge className={cn("text-xs", categoryColorClass.blue)}>
v{plugin.version}
</Badge>
) : (
<span className="text-xs text-muted-foreground">-</span>
);
},
meta: {
className: "hidden lg:table-cell",
},
},
{
header: "Category",
accessorKey: "category",
enableSorting: true,
sortingFn: "alphanumeric",
cell: ({ row }) => {
const plugin = row.original;
const badgeColor = getCategoryBadgeColor(plugin.category);
return allColumns;
};
return plugin.category ? (
<Badge
className={cn(
"text-xs",
categoryColorClass[badgeColor] || categoryColorClass.gray,
)}
>
{plugin.category}
</Badge>
) : (
<Badge className={cn("text-xs", categoryColorClass.gray)}>
Uncategorized
</Badge>
);
},
meta: {
className: "hidden lg:table-cell",
},
},
{
header: "Source",
accessorKey: "source",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
return (
<span className="text-xs text-muted-foreground">
{getSourceDisplayText(plugin.source)}
</span>
);
},
meta: {
className: "hidden xl:table-cell",
},
},
{
header: "Keywords",
accessorKey: "keywords",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
const keywords = plugin.keywords?.slice(0, 3) || [];
const remaining = (plugin.keywords?.length || 0) - 3;
return (
<div className="flex flex-wrap gap-1">
{keywords.map((keyword, index) => (
<Badge
key={index}
className={cn("text-xs", categoryColorClass.gray)}
>
{keyword}
</Badge>
))}
{remaining > 0 && (
<Badge className={cn("text-xs", categoryColorClass.gray)}>
+{remaining}
</Badge>
)}
</div>
);
},
meta: {
className: "hidden xl:table-cell",
},
},
{
header: "Install Command",
id: "install_command",
enableSorting: false,
cell: ({ row }) => {
const plugin = row.original;
const installCommand = formatInstallCommand(plugin);
return (
<div className="flex items-center space-x-2">
<code className="text-xs bg-muted px-2 py-1 rounded font-mono truncate max-w-[200px]">
{installCommand}
</code>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="secondary"
onClick={() => copyToClipboard(installCommand)}
className="h-7 w-7"
aria-label="Copy command"
>
<Copy className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Copy command</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
},
},
];