Skip to main content
Glama

Convex MCP server

Official
by get-convex
ReadonlyCode.tsx6.65 kB
import type { editor } from "monaco-editor"; import Editor, { DiffEditor, DiffEditorProps, EditorProps, } from "@monaco-editor/react"; import { RefObject, useEffect, useRef, useState } from "react"; import { useTheme } from "next-themes"; import { editorOptions } from "@common/elements/ObjectEditor/ObjectEditor"; // The editor will have a height of 100% and will scroll. type ParentHeight = { type: "parent"; }; // The editor will always have it's height set to the height of the content. // You can consider setting this to `false` if you are experiencing issues with // the editor growing infinitely in height in your layout. type ContentHeight = { type: "content"; // The editor's height will not exceed this value if it's defined. If the // content's length is greater than this height, then the editor's height will // be capped at this value and will scroll. If the content's height is less // than this value, then the editor will shrink to match the content height. maxHeightRem?: number; }; export type HighlightLines = { startLineNumber: number; endLineNumber: number; }; const maxHeightPixels = (heightObj: ContentHeight) => { const { maxHeightRem } = heightObj; if (!maxHeightRem) { return Number.MAX_SAFE_INTEGER; } const fontSize = parseFloat( getComputedStyle(document.documentElement).fontSize || "16", ); return Math.round(maxHeightRem * fontSize); }; function sharedEditorProps( height: ParentHeight | ContentHeight, prefersDark: boolean, disableLineNumbers: boolean, ): EditorProps & DiffEditorProps { return { height: "100%", theme: prefersDark ? "vs-dark" : "light", // Necessary to hide the cursor in the readonly editor. See globals.css className: "readonlyEditor", options: { ...editorOptions, readOnly: true, wordWrap: "on", domReadOnly: true, lineNumbers: disableLineNumbers ? "off" : "on", hover: { enabled: false }, scrollbar: { horizontalScrollbarSize: 8, verticalScrollbarSize: 8, alwaysConsumeMouseWheel: false, useShadows: false, vertical: height.type === "content" && height.maxHeightRem === undefined ? "hidden" : "visible", }, glyphMargin: !disableLineNumbers, lineDecorationsWidth: disableLineNumbers ? 0 : 10, lineNumbersMinChars: disableLineNumbers ? 0 : 5, folding: !disableLineNumbers, }, }; } function setupAutoHeight( editor: editor.ICodeEditor, ref: RefObject<HTMLDivElement>, maxHeight: number, variant: "editor" | "diff", ) { const updateHeight = (e: editor.IContentSizeChangedEvent) => { if (!e.contentHeightChanged || !ref.current) { return; } const contentHeight = Math.min(maxHeight, editor.getContentHeight()); // eslint-disable-next-line no-param-reassign ref.current.style.height = `${contentHeight}px`; editor.layout({ height: contentHeight, width: variant === "diff" ? ref.current.offsetWidth / 2 : ref.current.offsetWidth, }); }; editor.onDidContentSizeChange(updateHeight); } export type ReadonlyCodeProps = { code: string; language?: string; highlightLines?: HighlightLines; path: string; height?: ParentHeight | ContentHeight; disableLineNumbers?: boolean; }; export function ReadonlyCode({ code, language = "json", highlightLines, path, height = { type: "parent" }, disableLineNumbers = false, }: ReadonlyCodeProps) { const [editor, setEditor] = useState<any>(); useEffect(() => { if (highlightLines === undefined) { return; } // Paint the selected line. editor?.deltaDecorations( [], [ { range: highlightLines, options: { isWholeLine: true, marginClassName: "monacoLineHighlight", inlineClassName: "monacoLineHighlight", }, }, ], ); }, [editor, highlightLines, path]); const ref = useRef<HTMLDivElement>(null); // code.length * 18 is a hack from // https://github.com/microsoft/monaco-editor/issues/794#issuecomment-383523405 // If it's wrong (probably due to font size changes), worst case there will // be a bit of a UI flash from our incorrect guess to the correct value that's // set in `updateHeight` based on the actual content size below. let initialHeight; if (height.type === "content") { const contentHeightGuessPixels = (code?.split("\n").length ?? 0) * 18; initialHeight = { height: Math.min(contentHeightGuessPixels, maxHeightPixels(height)), }; } else { initialHeight = { height: "100%", }; } const { resolvedTheme: currentTheme } = useTheme(); const prefersDark = currentTheme === "dark"; return ( <div ref={ref} style={initialHeight} key={path}> <Editor value={code} path={path} language={language} onMount={(e) => { setEditor(e); if (height.type === "content") { setupAutoHeight(e, ref, maxHeightPixels(height), "editor"); } e.revealLineNearTop(highlightLines?.startLineNumber ?? 1); }} {...sharedEditorProps(height, prefersDark, disableLineNumbers)} /> </div> ); } export function ReadonlyCodeDiff({ originalCode, modifiedCode, language = "json", path, height = { type: "parent" }, }: { originalCode: string; modifiedCode: string; language?: string; path: string; height?: ParentHeight | ContentHeight; }) { const ref = useRef<HTMLDivElement>(null); // Since there is no simple way to pre-compute the initial height of a diff, // we default to 200px and wait for the first onMount event handler // to set the actual height const initialHeight = height.type === "content" ? { height: "200px" } : { height: "100%" }; const { resolvedTheme: currentTheme } = useTheme(); const prefersDark = currentTheme === "dark"; return ( <div ref={ref} style={initialHeight}> <DiffEditor original={originalCode} modified={modifiedCode} path={path} language={language} onMount={(e) => { if (height.type === "content") { for (const editor of [ e.getOriginalEditor(), e.getModifiedEditor(), ]) { setupAutoHeight(editor, ref, maxHeightPixels(height), "diff"); setupAutoHeight(editor, ref, maxHeightPixels(height), "diff"); } } }} {...sharedEditorProps(height, prefersDark, false)} /> </div> ); }

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/get-convex/convex-backend'

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