"use client";
import { useState } from "react";
import { Card, ModelSelectModal } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import Image from "next/image";
import ApiKeySelect from "./ApiKeySelect";
export default function DefaultToolCard({ toolId, tool, isExpanded, onToggle, baseUrl, apiKeys, activeProviders = [], cloudEnabled = false, tunnelEnabled = false }) {
const [copiedField, setCopiedField] = useState(null);
const [showModelModal, setShowModelModal] = useState(false);
const [modelValue, setModelValue] = useState("");
// Initialize state directly with computed value - no need for useEffect
const [selectedApiKey, setSelectedApiKey] = useState(() =>
apiKeys?.length > 0 ? apiKeys[0].key : ""
);
const replaceVars = (text) => {
const keyToUse = (selectedApiKey && selectedApiKey.trim())
? selectedApiKey
: (!cloudEnabled ? "sk_9router" : "your-api-key");
// Add /v1 suffix only if not already present (DRY - avoid duplicate)
const normalizedBaseUrl = baseUrl || "http://localhost:20128";
const baseUrlWithV1 = normalizedBaseUrl.endsWith("/v1")
? normalizedBaseUrl
: `${normalizedBaseUrl}/v1`;
return text
.replace(/\{\{baseUrl\}\}/g, baseUrlWithV1)
.replace(/\{\{apiKey\}\}/g, keyToUse)
.replace(/\{\{model\}\}/g, modelValue || "provider/model-id");
};
const { copy: copyToClipboard } = useCopyToClipboard();
const handleCopy = async (text, field) => {
await copyToClipboard(replaceVars(text), `toolcard-${field}`);
setCopiedField(field);
setTimeout(() => setCopiedField(null), 2000);
};
const handleSelectModel = (model) => {
setModelValue(model.value);
};
const hasActiveProviders = activeProviders.length > 0;
const renderApiKeySelector = () => (
);
const renderModelSelector = () => {
return (
setModelValue(e.target.value)}
placeholder="provider/model-id"
className="w-full sm:w-auto flex-1 px-3 py-2 bg-bg-secondary rounded-lg text-sm border border-border focus:outline-none focus:ring-1 focus:ring-primary/50"
/>
{modelValue && (
<>
>
)}
);
};
const renderNotes = () => {
if (!tool.notes || tool.notes.length === 0) return null;
return (
{tool.notes.map((note, index) => {
// Skip cloudCheck note if tunnel or cloud is enabled
if (note.type === "cloudCheck" && (cloudEnabled || tunnelEnabled)) return null;
const isWarning = note.type === "warning";
const isError = note.type === "cloudCheck" && !cloudEnabled && !tunnelEnabled;
let bgClass = "bg-blue-500/10 border-blue-500/30";
let textClass = "text-blue-600 dark:text-blue-400";
let iconClass = "text-blue-500";
let icon = "info";
if (isWarning) {
bgClass = "bg-yellow-500/10 border-yellow-500/30";
textClass = "text-yellow-600 dark:text-yellow-400";
iconClass = "text-yellow-500";
icon = "warning";
} else if (isError) {
bgClass = "bg-red-500/10 border-red-500/30";
textClass = "text-red-600 dark:text-red-400";
iconClass = "text-red-500";
icon = "error";
}
return (
);
})}
);
};
const canShowGuide = () => {
if (tool.requiresExternalUrl && !cloudEnabled && !tunnelEnabled) return false;
if (tool.requiresCloud && !cloudEnabled) return false;
return true;
};
const renderGuideSteps = () => {
if (!tool.guideSteps) return Coming soon...
;
return (
{renderNotes()}
{canShowGuide() && tool.guideSteps.map((item) => (
{item.step}
{item.title}
{item.desc &&
{item.desc}
}
{item.type === "apiKeySelector" && renderApiKeySelector()}
{item.type === "modelSelector" && renderModelSelector()}
{item.value && (
{replaceVars(item.value)}
{item.copyable && (
)}
)}
))}
{canShowGuide() && tool.codeBlock && (
{tool.codeBlock.language}
{replaceVars(tool.codeBlock.code)}
)}
);
};
const renderIcon = () => {
if (tool.image) {
return (
{ e.target.style.display = "none"; }}
/>
);
}
if (tool.icon) {
return {tool.icon};
}
return (
{ e.target.style.display = "none"; }}
/>
);
};
return (
{renderIcon()}
{tool.name}
{tool.description}
expand_more
{isExpanded && (
{renderGuideSteps()}
)}
setShowModelModal(false)}
onSelect={handleSelectModel}
selectedModel={modelValue}
activeProviders={activeProviders}
title="Select Model"
/>
);
}