| import React from "react"; |
| import { useTranslation } from "react-i18next"; |
| import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; |
| import { SyntaxHighlighter } from "../../../markdown/syntax-highlighter"; |
| import { CopyableContentWrapper } from "#/components/shared/buttons/copyable-content-wrapper"; |
| import { MAX_CONTENT_LENGTH } from "#/components/conversation-events/chat/event-content-helpers/shared"; |
| import { I18nKey } from "#/i18n/declaration"; |
|
|
| interface CodeBlockProps { |
| code: string; |
| |
| language?: string; |
| |
| copy?: boolean; |
| |
| placeholder?: string; |
| |
| expandable?: boolean; |
| |
| wrapLongLines?: boolean; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function CodeBlock({ |
| code, |
| language, |
| copy = true, |
| placeholder, |
| expandable = false, |
| wrapLongLines = false, |
| }: CodeBlockProps) { |
| const { t } = useTranslation("openhands"); |
| const [isExpanded, setIsExpanded] = React.useState(false); |
| const isTruncated = code.length > MAX_CONTENT_LENGTH; |
| const display = |
| isTruncated && !(expandable && isExpanded) |
| ? `${code.slice(0, MAX_CONTENT_LENGTH)}…` |
| : code; |
| const text = display.trim() || placeholder || ""; |
| const canCopy = copy && code.trim().length > 0; |
| const toggleLabel = isExpanded |
| ? t(I18nKey.BUTTON$COLLAPSE) |
| : t(I18nKey.BUTTON$EXPAND); |
|
|
| const block = ( |
| <SyntaxHighlighter |
| className="rounded-lg text-xs" |
| style={vscDarkPlus} |
| language={language} |
| PreTag="div" |
| wrapLongLines={wrapLongLines} |
| customStyle={wrapLongLines ? { whiteSpace: "pre-wrap" } : undefined} |
| codeTagProps={ |
| wrapLongLines ? { style: { whiteSpace: "pre-wrap" } } : undefined |
| } |
| > |
| {text} |
| </SyntaxHighlighter> |
| ); |
|
|
| return ( |
| <div className="flex flex-col gap-1"> |
| {canCopy ? ( |
| <CopyableContentWrapper text={code}>{block}</CopyableContentWrapper> |
| ) : ( |
| block |
| )} |
| {expandable && isTruncated && ( |
| <button |
| type="button" |
| onClick={() => setIsExpanded((prev) => !prev)} |
| className="self-start text-xs text-muted transition-colors hover:text-white hover:underline" |
| > |
| {toggleLabel} |
| </button> |
| )} |
| </div> |
| ); |
| } |
|
|