搭建网页版ChatGPT(四)

搭建网页版 ChatGPT(四)

前言

我们继续上一篇的内容,前面我们完成了对话功能以及请求的处理,本小节主要介绍如何渲染 chatGPT 返回的代码,让代码高亮。

还记得前面我们讲过的 mdx 的实时编译吗, 现在我们就要用到了。如果没看过的可以翻翻我前面的几篇文章,讲的很细。

让代码高亮

这里就不重复前面的 mdx 的内容了,使用起来非常简单

const ChatBot = ({ content, date }: ChatBotProps) => {
  const [{ Content }, setState] =
    useState <
    any >
    {
      Content: undefined,
    };
  useEffect(() => {
    compileAndRun(content, 'md').then(({ content, error }) => {
      setState((state: any) => ({
        Content: content || state.Content,
      }));
    });
  }, [content]);
  return (
    <>
      <div className='w-full'>
        {Content ? <Content components={MDXComponents} /> : null}
      </div>
    </>
  );
};

export default ChatBot;

小彩蛋

下面讲下如何实现代码复制功能,以下是复制按钮组件代码:

export function MDXCopyButton({
  onCope,
  style = {
    position: 'absolute',
    top: '10px',
    right: '10px',
    width: '1.1em',
    height: '1.1em',
  },
  className,
}: {
  onCope: (fn: any) => void;
  style?: React.CSSProperties;
  className?: string;
}) {
  const [copied, setCopied] = React.useState(false);

  const handleCope = async () => {
    onCope(function (code: any) {
      if (code) {
        setCopied(true);
        copyToClipboard(code).then(() => {
          setCopied(false);
        });
      }
    });
  };
  return (
    <button
      type='button'
      title='Copy code'
      className={className}
      style={style}
      onClick={() => handleCope()}
    >
      <svg
        fill='none'
        stroke='currentColor'
        viewBox='0 0 24 24'
        xmlns='http://www.w3.org/2000/svg'
      >
        {copied ? (
          <path
            strokeLinecap='round'
            strokeLinejoin='round'
            strokeWidth={2}
            d='M5 13l4 4L19 7'
          />
        ) : (
          <path
            strokeLinecap='round'
            strokeLinejoin='round'
            strokeWidth='1.6px'
            d='M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z'
          />
        )}
      </svg>
    </button>
  );
}

const copyToClipboard = async (text: string) => {
  if (!navigator.clipboard) {
    return;
  }
  await navigator.clipboard.writeText(text);
};

使用组件

export default memo(function CodeBlockWrapper(children): any {
  const ref = useRef<HTMLPreElement>(null);
  return (
    <pre
      ref={ref}
      className='align-start flex'
    >
      <MDXCopyButton
        onCope={(fn: any) => {
          if (ref.current) {
            fn(ref.current.innerText);
          }
        }}
      />
      <code>{children}</code>
    </pre>
  );
});

最后

如果喜欢我的文章,欢迎关注我,同时也欢迎关注我的个人网站,里面有多个我一直迭代的项目,

还有我的 b 站,我会定期在 b 站分享一些你意想不到的知识。