Skip to content

Commit b9201d2

Browse files
authored
Refactor action config components to use dynamic plugin system (#63)
- Replace hardcoded action lists with dynamic plugin registry lookups - Add lazy loading for plugin test functions to prevent Node.js packages from being bundled in client code - Update IntegrationIcon to dynamically resolve icons from plugins - Fix Firecrawl icon SVG (was showing wrong icon) - Add Resend icon SVG component with currentColor support - Keep System actions (HTTP Request, Database Query, Condition) hardcoded since they have no plugins Co-authored-by: Benjamin Sabic <bensabic@users.noreply.github.com>
1 parent 20d789d commit b9201d2

File tree

12 files changed

+225
-691
lines changed

12 files changed

+225
-691
lines changed

components/ui/integration-icon.tsx

Lines changed: 47 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
1-
import { Database, Flame, Sparkles } from "lucide-react";
1+
"use client";
2+
3+
import { Database, HelpCircle } from "lucide-react";
24
import Image from "next/image";
5+
import type { IntegrationType } from "@/lib/db/integrations";
36
import { cn } from "@/lib/utils";
7+
import { getIntegration } from "@/plugins";
48

59
interface IntegrationIconProps {
6-
integration: "linear" | "resend" | "slack" | "vercel" | "database" | "ai-gateway" | "firecrawl";
10+
integration: string;
711
className?: string;
812
}
913

10-
// Inline SVG components for icons that need currentColor support
11-
function ResendIcon({ className }: { className?: string }) {
12-
return (
13-
<svg
14-
className={className}
15-
fill="currentColor"
16-
height="12"
17-
viewBox="0 0 600 600"
18-
width="12"
19-
xmlns="http://www.w3.org/2000/svg"
20-
>
21-
<path d="M186 447.471V154H318.062C336.788 154 353.697 158.053 368.79 166.158C384.163 174.263 396.181 185.443 404.845 199.698C413.51 213.672 417.842 229.604 417.842 247.491C417.842 265.938 413.51 282.568 404.845 297.381C396.181 311.915 384.302 323.375 369.209 331.759C354.117 340.144 337.067 344.337 318.062 344.337H253.917V447.471H186ZM348.667 447.471L274.041 314.99L346.99 304.509L430 447.471H348.667ZM253.917 289.835H311.773C319.04 289.835 325.329 288.298 330.639 285.223C336.229 281.869 340.421 277.258 343.216 271.388C346.291 265.519 347.828 258.811 347.828 251.265C347.828 243.718 346.151 237.15 342.797 231.56C339.443 225.691 334.552 221.219 328.124 218.144C321.975 215.07 314.428 213.533 305.484 213.533H253.917V289.835Z" />
22-
</svg>
23-
);
24-
}
25-
14+
// Inline SVG for Vercel icon (special case - no plugin)
2615
function VercelIcon({ className }: { className?: string }) {
2716
return (
2817
<svg
@@ -38,61 +27,56 @@ function VercelIcon({ className }: { className?: string }) {
3827
);
3928
}
4029

41-
function FirecrawlIcon({ className }: { className?: string }) {
42-
return (
43-
<svg
44-
className={className}
45-
fill="currentColor"
46-
height="12"
47-
viewBox="0 0 50 72"
48-
width="12"
49-
xmlns="http://www.w3.org/2000/svg"
50-
>
51-
<path
52-
d="M41.7154 23.1929C38.9531 24.0129 36.8707 25.8677 35.3457 27.8826C35.0183 28.3151 34.3358 27.9901 34.4658 27.4601C37.3856 15.4534 33.5283 5.47401 21.5039 0.561817C20.894 0.311833 20.259 0.859299 20.419 1.49926C25.8887 23.4604 2.88236 21.608 5.78971 46.504C5.83971 46.9314 5.35973 47.2239 5.00975 46.9739C3.9198 46.1915 2.70237 44.5591 1.86741 43.4116C1.62242 43.0742 1.09245 43.1692 0.979951 43.5716C0.314984 45.9765 0 48.2413 0 50.4912C0 59.2407 4.49727 66.9427 11.3044 71.4074C11.6944 71.6624 12.1944 71.2974 12.0619 70.8499C11.7119 69.675 11.5144 68.4351 11.4994 67.1527C11.4994 66.3652 11.5494 65.5603 11.6719 64.8103C11.9569 62.9254 12.6119 61.1305 13.7118 59.4957C17.4841 53.8335 25.0462 48.3638 23.8388 40.9368C23.7613 40.4668 24.3163 40.1569 24.6663 40.4793C29.9935 45.3465 31.0485 51.8936 30.1735 57.7658C30.0985 58.2757 30.7385 58.5482 31.061 58.1482C31.8759 57.1283 32.8709 56.2334 33.9533 55.5609C34.2233 55.3934 34.5833 55.5209 34.6858 55.8209C35.2882 57.5733 36.1832 59.2182 37.0281 60.8631C38.0381 62.8404 38.5756 65.0978 38.4906 67.4877C38.4481 68.6501 38.2556 69.775 37.9331 70.8449C37.7956 71.2974 38.2906 71.6749 38.6881 71.4149C45.5002 66.9502 50 59.2482 50 50.4937C50 47.4514 49.4675 44.4691 48.4601 41.6743C46.3477 35.8121 40.988 31.4099 42.3429 23.7704C42.4079 23.4054 42.0704 23.0879 41.7154 23.1929Z"
53-
fill="#FA5D19"
54-
/>
55-
</svg>
56-
);
57-
}
30+
// Special icons for integrations without plugins (database, vercel, ai-gateway)
31+
const SPECIAL_ICONS: Record<
32+
string,
33+
React.ComponentType<{ className?: string }>
34+
> = {
35+
database: Database,
36+
vercel: VercelIcon,
37+
"ai-gateway": VercelIcon,
38+
};
5839

5940
export function IntegrationIcon({
6041
integration,
6142
className = "h-3 w-3",
6243
}: IntegrationIconProps) {
63-
const iconMap = {
64-
linear: "/integrations/linear.svg",
65-
slack: "/integrations/slack.svg",
66-
};
67-
68-
// Use inline SVG for resend and vercel to support currentColor
69-
if (integration === "resend") {
70-
return <ResendIcon className={cn("text-foreground", className)} />;
44+
// Check for special icons first (integrations without plugins)
45+
const SpecialIcon = SPECIAL_ICONS[integration];
46+
if (SpecialIcon) {
47+
return <SpecialIcon className={cn("text-foreground", className)} />;
7148
}
7249

73-
if (integration === "vercel") {
74-
return <VercelIcon className={cn("text-foreground", className)} />;
75-
}
50+
// Look up plugin from registry
51+
const plugin = getIntegration(integration as IntegrationType);
7652

77-
if (integration === "database") {
78-
return <Database className={cn("text-foreground", className)} />;
79-
}
53+
if (plugin) {
54+
const { icon } = plugin;
8055

81-
if (integration === "ai-gateway") {
82-
return <Sparkles className={cn("text-foreground", className)} />;
83-
}
56+
// Handle image type icons
57+
if (icon.type === "image") {
58+
return (
59+
<Image
60+
alt={`${plugin.label} logo`}
61+
className={className}
62+
height={12}
63+
src={icon.value}
64+
width={12}
65+
/>
66+
);
67+
}
8468

85-
if (integration === "firecrawl") {
86-
return <FirecrawlIcon className={cn("text-foreground", className)} />;
69+
// Handle SVG component icons
70+
if (icon.type === "svg" && icon.svgComponent) {
71+
const SvgComponent = icon.svgComponent;
72+
return <SvgComponent className={cn("text-foreground", className)} />;
73+
}
74+
75+
// Handle lucide icons - these are already React components in plugin.actions
76+
// For plugin-level icons, we would need to dynamically import lucide icons
77+
// For now, fall through to default
8778
}
8879

89-
return (
90-
<Image
91-
alt={`${integration} logo`}
92-
className={className}
93-
height={12}
94-
src={iconMap[integration as "linear" | "slack"]}
95-
width={12}
96-
/>
97-
);
80+
// Fallback for unknown integrations
81+
return <HelpCircle className={cn("text-foreground", className)} />;
9882
}

0 commit comments

Comments
 (0)