"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 (
{icon}

{note.text}

); })}
); }; 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 ( {tool.name} { e.target.style.display = "none"; }} /> ); } if (tool.icon) { return {tool.icon}; } return ( {tool.name} { 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" />
); }