Skip to main content
Glama
IDE.tsx3.69 kB
'use client'; import { type FC, type HTMLAttributes, useEffect, useState } from 'react'; import { cn } from '../../utils/cn'; import { Container } from '../Container'; import { WithResizer } from '../WithResizer'; import { FileTree } from './FileTree'; import { MarkdownRenderer } from './MarkDownRender'; export type IDEProps = { pages: { path: string; content: string; isOpen?: boolean; }[]; isDarkMode?: boolean; activeTab?: number; } & HTMLAttributes<HTMLDivElement>; export const IDE: FC<IDEProps> = ({ pages: initialPages, isDarkMode, className, activeTab: defaultActiveTab, ...props }) => { const [pages, setPages] = useState(initialPages); const tabs = pages.filter(({ isOpen }) => isOpen); const firstTabIndex = tabs.findIndex(({ isOpen }) => isOpen); const [activeTab, setActiveTab] = useState(defaultActiveTab ?? firstTabIndex); useEffect(() => { setActiveTab(defaultActiveTab ?? firstTabIndex); }, [initialPages, defaultActiveTab]); const { content, path } = pages[activeTab]; const filePaths = initialPages.map(({ path: title }) => title); const handleClickFile = (title: string) => { const page = pages.find(({ path: tabTitle }) => tabTitle === title); if (!page) return; const newPages = pages.map((page) => { if (page.path === title) { return { ...page, isOpen: true }; } return page; }); setPages(newPages); const newPageIndex = newPages.findIndex( ({ path: tabTitle }) => tabTitle === title ); setActiveTab(newPageIndex); }; return ( <Container className={cn( 'flex size-full flex-col justify-start overflow-hidden shadow-lg', className )} roundedSize="3xl" transparency="none" {...props} > <div className="flex w-auto flex-row items-center justify-start gap-1 bg-neutral-200 text-neutral text-xs dark:bg-neutral-950"> <div className="mx-2 flex items-center justify-start gap-2 p-1"> <div className="size-3 rounded-full bg-red-500" /> <div className="size-3 rounded-full bg-yellow-500" /> <div className="size-3 rounded-full bg-green-500" /> </div> <div className="flex size-full overflow-y-auto"> {tabs.map(({ path }, index) => { const fullPath = path.split('/'); const title = fullPath[fullPath.length - 1]; const isActive = index === activeTab; return ( <button className={cn( 'flex h-8 min-w-20 items-center justify-start px-3 py-1 transition', isActive ? 'bg-card' : 'cursor-pointer bg-neutral-200 hover:bg-neutral-300 dark:bg-neutral-950' )} key={title} onClick={() => setActiveTab(index)} > {title} </button> ); })} </div> </div> <div className="relative flex size-full flex-1 flex-row justify-start"> <div className="absolute top-0 left-0 size-full"> <div className="flex size-full"> <WithResizer initialWidth={150}> <FileTree filesPaths={filePaths} activeFile={path} onClickFile={handleClickFile} /> </WithResizer> <div className="size-full flex-1 overflow-auto pt-2 text-xs"> <MarkdownRenderer isDarkMode={isDarkMode}> {content} </MarkdownRenderer> </div> </div> </div> </div> </Container> ); };

Latest Blog Posts

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/aymericzip/intlayer'

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