Skip to main content
Glama

mcp-google-sheets

markdown.tsx7.17 kB
import { useMutation } from '@tanstack/react-query'; import { t } from 'i18next'; import { Check, Copy, Info, AlertTriangle, Lightbulb } from 'lucide-react'; import React, { useState } from 'react'; import ReactMarkdown from 'react-markdown'; import breaks from 'remark-breaks'; import gfm from 'remark-gfm'; import { cn } from '@/lib/utils'; import { MarkdownVariant } from '@activepieces/shared'; import { Alert, AlertDescription } from '../ui/alert'; import { Button } from '../ui/button'; import { useToast } from '../ui/use-toast'; function applyVariables(markdown: string, variables: Record<string, string>) { if (typeof markdown !== 'string') { return ''; } let result = markdown.split('<br>').join('\n'); result = result.replace(/\{\{(.*?)\}\}/g, (_, variableName) => { return variables[variableName] ?? ''; }); return result; } type MarkdownProps = { markdown: string | undefined; variables?: Record<string, string>; variant?: MarkdownVariant; className?: string; loading?: string; }; const Container = ({ variant, children, }: { variant?: MarkdownVariant; children: React.ReactNode; }) => { return ( <Alert className={cn('rounded-md border', { 'bg-warning-100 text-warning-300 border-none': variant === MarkdownVariant.WARNING, 'bg-success-100 text-success-300 border-none': variant === MarkdownVariant.TIP, 'p-0 bg-transparent border-none': variant === MarkdownVariant.BORDERLESS, })} > {variant !== MarkdownVariant.BORDERLESS && ( <> {(variant === MarkdownVariant.INFO || variant === undefined) && ( <Info className="w-4 h-4 mt-1" /> )} {variant === MarkdownVariant.WARNING && ( <AlertTriangle className="w-4 h-4 mt-1" /> )} {variant === MarkdownVariant.TIP && ( <Lightbulb className="w-4 h-4 mt-1" /> )} </> )} <AlertDescription className="flex-grow w-full"> {children} </AlertDescription> </Alert> ); }; const ApMarkdown = React.memo( ({ markdown, variables, variant, className, loading }: MarkdownProps) => { const [copiedText, setCopiedText] = useState<string | null>(null); const { toast } = useToast(); const { mutate: copyToClipboard } = useMutation({ mutationFn: async (text: string) => { await navigator.clipboard.writeText(text); setCopiedText(text); await new Promise((resolve) => setTimeout(resolve, 1000)); setCopiedText(null); }, onError: () => { toast({ title: t('Failed to copy to clipboard'), duration: 3000, }); }, }); if (loading && loading.length > 0) { return ( <Container variant={variant}> <div className="flex items-center gap-2">{loading}</div> </Container> ); } if (!markdown) { return null; } const markdownProcessed = applyVariables(markdown, variables ?? {}); return ( <Container variant={variant}> <ReactMarkdown className={cn('flex-grow w-full ', className)} remarkPlugins={[gfm, breaks]} components={{ code(props) { const isLanguageText = props.className?.includes('language-text'); if (!isLanguageText) { return <code {...props} className="text-wrap" />; } const codeContent = String(props.children).trim(); const isCopying = codeContent === copiedText; return ( <div className="relative w-full items-center flex bg-background border border-solid text-sm rounded block w-full gap-1 p-1.5"> <input type="text" className="grow bg-background" value={codeContent} disabled /> <Button variant="ghost" className="bg-background rounded p-2 inline-flex items-center justify-center h-8" onClick={() => copyToClipboard(codeContent)} > {isCopying ? ( <Check className="w-3 h-3" /> ) : ( <Copy className="w-3 h-3" /> )} </Button> </div> ); }, h1: ({ node, ...props }) => ( <h1 className="scroll-m-20 text-xl font-extrabold tracking-tight lg:text-3xl" {...props} /> ), h2: ({ node, ...props }) => ( <h2 className="scroll-m-20 text-lg text-xl font-semibold tracking-tight first:mt-0" {...props} /> ), h3: ({ node, ...props }) => ( <h3 className="scroll-m-20 text-lg font-semibold tracking-tight" {...props} /> ), p: ({ node, ...props }) => ( <p className="leading-7 [&:not(:first-child)]:mt-2 w-full" {...props} /> ), ul: ({ node, ...props }) => ( <ul className="mt-4 ml-6 list-disc [&>li]:mt-2" {...props} /> ), ol: ({ node, ...props }) => ( <ol className="mt-4 ml-6 list-decimal [&>li]:mt-2" {...props} /> ), li: ({ node, ...props }) => <li {...props} />, a: ({ node, ...props }) => ( <a className="font-medium text-primary underline underline-offset-4" target="_blank" rel="noreferrer noopener" {...props} /> ), blockquote: ({ node, ...props }) => ( <blockquote className="mt-4 first:mt-0 border-l-2 pl-6 italic" {...props} /> ), hr: ({ node, ...props }) => ( <hr className="my-4 border-t border-border/50" {...props} /> ), img: ({ node, ...props }) => <img className="my-8" {...props} />, b: ({ node, ...props }) => <b {...props} />, em: ({ node, ...props }) => <em {...props} />, table: ({ node, ...props }) => ( <table className="w-full my-4 border-collapse" {...props} /> ), thead: ({ node, ...props }) => ( <thead className="bg-muted" {...props} /> ), tr: ({ node, ...props }) => ( <tr className="border-b border-border" {...props} /> ), th: ({ node, ...props }) => ( <th className="text-left p-2 font-medium" {...props} /> ), td: ({ node, ...props }) => <td className="p-2" {...props} />, }} > {markdownProcessed.trim()} </ReactMarkdown> </Container> ); }, ); ApMarkdown.displayName = 'ApMarkdown'; export { ApMarkdown };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server