vue3 实现 chatgpt 的打字机效果

在做 chatgpt 镜像站的时候,发现有些镜像站是没做打字机的光标效果的,就只是文字输出,是他们不想做吗?反正我想做。于是我仔细研究了一下,实现了打字机效果加光标的效果,现在分享一下我的解决方案以及效果图

Kapture 2023-04-14 at 14.02.32.gif

共识

首先要明确一点,chatgpt 返回的文本格式是 markdown 的,最基本的渲染方式就是把 markdown 文本转换为 HTML 文本,然后 v-html 渲染即可。这里的转换和代码高亮以及防 XSS 攻击用到了下面三个依赖库:

  • marked 将markdwon 转为 html
  • highlight 处理代码高亮
  • dompurify 防止 XSS 攻击

同时我们是可以在 markdown 中写 html 元素的,这意味着我们可以直接把光标元素放到最后!

将 markdown 转为 html 并处理代码高亮

先贴代码

MarkdownRender.vue

<script setup>
import {computed} from 'vue';
import DOMPurify from 'dompurify';
import {marked} from 'marked';
import hljs from '//cdn.staticfile.org/highlight.js/11.7.0/es/highlight.min.js';
import mdInCode from "@/utils/mdInCode"; // 用于判断是否显示光标const props = defineProps({// 输入的 markdown 文本text: {type: String,default: ""},// 是否需要显示光标?比如在消息流结束后是不需要显示光标的showCursor: {type: Boolean,default: false}
})// 配置高亮
marked.setOptions({highlight: function (code, lang) {try {if (lang) {return hljs.highlight(code, {language: lang}).value} else {return hljs.highlightAuto(code).value}} catch (error) {return code}},gfmtrue: true,breaks: true
})// 计算最终要显示的 html 文本
const html = computed(() => {// 将 markdown 转为 htmlfunction trans(text) {return DOMPurify.sanitize(marked.parse(text));}// 光标元素,可以用 css 美化成你想要的样子const cursor = '<span class="cursor"></span>';if (props.showCursor) {// 判断 AI 正在回的消息是否有未闭合的代码块。const inCode = mdInCode(props.text)if (inCode) {// 有未闭合的代码块,不显示光标return trans(props.text);} else {// 没有未闭合的代码块,将光标元素追加到最后。return trans(props.text + cursor);}} else {// 父组件明确不显示光标return trans(props.text);}
})</script><template><!-- tailwindcss:leading-7 控制行高为1.75rem --><div v-html="html" class="markdown leading-7"></div>
</template><style lang="postcss">
/** 设置代码块样式 **/
.markdown pre {@apply bg-[#282c34] p-4 mt-4 rounded-md text-white w-full overflow-x-auto;
}
.markdown code {width: 100%;
}/** 控制段落间的上下边距 **/
.markdown p {margin: 1.25rem 0;
}
.markdown p:first-child {margin-top: 0;
}/** 小代码块样式,对应 markdown 的 `code` **/
.markdown :not(pre) > code {@apply bg-[#282c34] px-1 py-[2px] text-[#e06c75] rounded-md;
}/** 列表样式 **/
.markdown ol {list-style-type: decimal;padding-left: 40px;
}
.markdown ul {list-style-type: disc;padding-left: 40px;
}/** 光标样式 **/
.markdown .cursor {display: inline-block;width: 2px;height: 20px;@apply bg-gray-800 dark:bg-gray-100;animation: blink 1.2s step-end infinite;margin-left: 2px;vertical-align: sub;
}
@keyframes blink {0% {opacity: 1;}50% {opacity: 0;}100% {opacity: 1;}
}
</style>

可以发现最基本的 markdown 显示还是挺简单的,话就不多说了,都在注释里。

我想你也许对判断消息中的代码块是否未闭合更感兴趣,那么就继续看下去吧!

代码块是否未闭合

markdown 有两种代码块,一种是 `code` ,另一种是 ``` code ```,我叫他小代码块和大代码块。

一开始我是想用正则去判断的,但是奈何有点复杂,我实在想不出应该如何去编写正则,让 chatgpt 写的正则也会判断失败,而且还要考虑到转义符,就算写出了正则,估计也会很复杂和难以维护。

经过短暂的苦思冥想后,我想到了之前在 《Vue.js设计与实现》 中看到的用有限元状态机解析 html 文本的方案。

这里有请 chatgpt 简单介绍一下有限元状态机:

有限元状态机(finite element state machine)是一种基于状态机的建模技术,它可以将系统的行为描述为一组状态和状态之间的转换。每个状态可以对应系统内的一种状态,而状态之间的转换则表示系统内部的转换过程。有限元状态机还可以用于系统的可靠性分析和测试,同时也可以作为软件工程中的一种建模工具,帮助代码实现、测试和调试等方面。

在实际应用中,有限元状态机通常是通过自动化工具生成,并且可以用于控制系统、网络通信协议、软件接口等方面的设计和测试。由于有限元状态机具有自动化生成和完备性检查等优点,因此它已经成为了一种广泛应用的建模与测试工具。

请站在web前端的视角下进行介绍

从前端开发的角度来看,有限元状态机可以用于构建复杂的交互式Web应用程序。在Web应用程序中,有限元状态机可以描述用户界面的不同状态(例如,登录、注册、未登录等),以及状态之间的转换(例如,从登录状态转换到注册状态)。

通常情况下,前端工程师使用JavaScript实现有限元状态机,包括定义状态、定义状态之间的转换规则、处理状态转换时的行为等。使用有限元状态机可以帮助前端工程师更好地掌控应用程序的状态,从而减少代码中的复杂条件分支和无法预测的行为。同时,有限元状态机也可以帮助前端团队共同理解应用程序的状态和转换规则,从而更好地协作开发和维护Web应用程序。

总之,有限元状态机是一种非常有用的前端开发技术,可以帮助前端工程师更好地构建和管理Web应用程序的状态和行为,提高应用程序的可靠性和用户体验。

回到正题,我可以一点一点的从头开始去解析 markdown 文本。想象这么一个简单的状态转换流程:

  • 初始状态为文本状态。
  • 遇到代码块标记,文本状态转换到代码块开始状态。
  • 再次遇到代码块标记,从代码块开始状态转换到文本状态。

不过现实要更复杂一点,我们有小代码块和大代码块。有限元状态机的妙处就在这里,当处在小代码块状态的时候,我们不需要操心大代码块和正常文本的事,他的下一个状态只能是遇到小代码块的闭合标签,进入文本状态。

理解了这些,再来看我的源码,才会发现他的精妙。

const States = {text: 0, // 文本状态codeStartSm: 1, // 小代码块状态codeStartBig: 2, // 大代码块状态
}/*** 判断 markdown 文本中是否有未闭合的代码块* @param text* @returns {boolean}*/
function isInCode(text) {let state = States.textlet source = textlet inStart = true // 是否处于文本开始状态,即还没有消费过文本while (source) { // 当文本被解析消费完后,就是个空字符串了,就能跳出循环let char = source.charAt(0) // 取第 0 个字switch (state) {case States.text:if (/^\n?```/.test(source)) {// 以 ```或者 \n```开头。表示大代码块开始。// 一般情况下,代码块前面都需要换行。但是如果是在文本的开头,就不需要换行。if (inStart || source.startsWith('\n')) {state = States.codeStartBig}source = source.replace(/^\n?```/, '')} else if (char === '\\') {// 遇到转义符,跳过下一个字符source = source.slice(2)} else if (char === '`') {// 以 ` 开头。表示小代码块开始。state = States.codeStartSmsource = source.slice(1)} else {// 其他情况,直接消费当前字符source = source.slice(1)}inStart = falsebreakcase States.codeStartSm:if (char === '`') {// 遇到第二个 `,表示代码块结束state = States.textsource = source.slice(1)} else if (char === '\\') {// 遇到转义符,跳过下一个字符source = source.slice(2)} else {// 其他情况,直接消费当前字符source = source.slice(1)}breakcase States.codeStartBig:if (/^\n```/.test(source)) {// 遇到第二个 ```,表示代码块结束state = States.textsource = source.replace(/^\n```/, '')} else {// 其他情况,直接消费当前字符source = source.slice(1)}break}}return state !== States.text
}export default isInCode

到这里,就已经实现了一个 chatgpt 消息渲染了。喜欢的话点个赞吧!谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31060.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Haproxy--高可用代理

一、haproxy简介 软件&#xff1a;haproxy---主要是做负载均衡的七层&#xff0c;也可以做四层负载均衡。 负载均衡是通过OSI协议对应的。 7层负载均衡&#xff1a;用的是7层http协议&#xff1b; 4层负载均衡&#xff1a;用的是tcp协议加端口号做的负载均衡。 ha-proxy概…

2021-03-11

idea创建mybatis的maven项目 用idea创建Maven Web项目 添加依赖 创建编写实体类 创建持久层接口 创建SqlMapConfing.xml

mybatis初学

创建一个mybatis的maven项目 环境&#xff1a;IDEA19.3.3&#xff0b;jdk1.8mysq 新建maven项目 项目名字 maven 大体框架 导包&#xff08;要在pom.xml中dependencies标签里面&#xff09; 2 <!-- mybatis核心包 -->3 <dependency>4 <…

2021-03-21

MyBatis-Plus创建 创建数据库 -- 创建测试表 CREATE TABLE tb_user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_name varchar(20) NOT NULL COMMENT 用户名, password varchar(20) NOT NULL COMMENT 密码, name varchar(30) DEFAULT NULL COMMENT 姓名, …

基于 Go 语言开发在线论坛

&#xff08;一&#xff09;&#xff1a;整体设计与数据模型 通过一个简单的在线论坛项目帮助大家从项目实际开发维度快速了解 Go Web 编程的全貌&#xff0c;然后再各个击破&#xff0c;深入介绍请求、响应、视图、数据库、Web 服务、测试、部署等各个模块的细节。 功能需求…

从智能对话系统导论,到如何设计第一个对话机器人

从智能对话系统导论&#xff0c;到如何设计第一个对话机器人 一、智能对话系统导论1、生活中的 Conversational AI2、一种新的人机交互方式3、一些关于 Conversational AI 的数据4、对话机器人行业产业链及产业图谱5、课程主要部分 二、Conversational AI 概览1、什么是Convers…

ChitChat论坛

《Go web编程》ChitChat论坛 本文摘自《Go web编程》 京东购书&#xff1a;https://item.jd.com/12252845.html 上一章在末尾展示了一个非常简单的Go Web应用&#xff0c;但是因为该应用只是一个Hello World程序&#xff0c;所以它实际上并没有什么用处。在本章中&#xff0c;我…

微信聊天机器人,不使用iChat,可以群聊

目录 1. 微信聊天界面截图 2. 图片文字识别 3. 获取最新消息 3.1 独聊 3.2 群聊 4. 机器人聊天系统 5. 成果展示 6. 全部代码 本文参考大神【喵王叭】的文章&#xff1a;python实现微信、QQ聊天自动回复【纯物理】_喵王叭的博客-CSDN博客_python自动回复纯物理方式实现微…

【论文翻译】2020.8 清华大学AI课题组——大型中文短文本对话数据集(A Large-Scale Chinese Short-Text Conversation Dataset)

大型中文短文本对话数据集 写在前面&#xff1a; 研究用&#xff0c;原创翻译&#xff0c;转载请标明出处&#xff1b;第一次译文&#xff0c;之后会跟进完善。侵删。 今年暑假末&#xff0c;清华大学公开了大型对话数据集及预训练模型。该数据集融合各大社交媒体对话数据库&am…

头条原创文章一键转换剪映生成视频

最近一段时间某更新了一个功能就是自己发布的文章可以在后台通过视频平台的功能一键生成短视频。 但是这个功能只能支持自己的写的文章,而且只能在本平台发布视频,还暂时不能同步到其他平台。而且必须是自己的原创文章哟,不是话会有一些问题。 这个做了几个视频之后发出来…

自媒体必备视频剪辑SDK,操作简单,功能强大,让每个精彩瞬间都能锦上添花!

VESDK DELUXE介绍&#xff1a; 一个全能好用的视频编辑工具&#xff0c;帮你轻松剪出美好生活。VESDK Deluxe APP视频编辑SDK集手机视频拍摄和视频剪辑主要功能于一体,同时包含手机端视频配音配乐&#xff0c;字幕特效&#xff0c;滤镜&#xff0c;转场特效等各种功能&#xff…

抖音之电脑版剪映的使用

本内容介绍剪映功能的使用以及一些个人剪映作品。紫色文字是超链接&#xff0c;点击自动跳转至相关博文。持续更新&#xff0c;原创不易&#xff01; 目录&#xff1a; 一、抖音&#xff1a;逝雪情感的剪映作品 1、曾经以为离不开的是微信 2、半生走过 3、夏日田园 二、音…

剪映专业版 - 你的短视频剪辑小帮手!附Mac/Win地址!

剪映专业版&#xff1a;做精品视频你不一定要用 FCPX &#xff01;随着短视频的崛起&#xff0c;市面上的剪辑软件也越来越多。除我们熟知的Premiere、Final Cut Pro等专业剪辑软件外&#xff0c;也有很多轻松易上手的App等待我们选择。近期&#xff0c;剪映推出了全新的专业版…

剪映电脑版详细使用教程,让视频剪辑变得更简单了

这几天关于剪映电脑版的消息非常多&#xff0c;相比于专业成熟的视频剪辑软件&#xff0c;但大家对这款剪映似乎特别感兴趣&#xff0c;小编也抽时间简单使用了一下&#xff0c;相比于adobe Premiere 和达芬奇来说&#xff0c;真的特别简单上手&#xff0c;结合了手机版的触摸与…

剪映app怎么剪辑视频

剪映是一款非常好用的手机上的视频剪辑软件&#xff0c;拥有这款软件的话&#xff0c;用户们即使是在手机上也能够快速方便的进行视频剪辑哦&#xff0c;但是有很多用户们没有剪辑过视频&#xff0c;不会使用剪映这款软件等&#xff0c;那么剪映app具体要怎么操作使用呢&#x…

AIz作画阶段小结后续内容安排

效果呈现 视频效果&#xff1a; 1.小说插画 2.动作系列 图画效果&#xff1a; 1.场景画面&#xff08;6张左右&#xff09; 2.人物画面&#xff08;6张左右&#xff0c;可以更多&#xff0c;可爱、知性、性感、古典、游戏、二次元&#xff09; 3.插画设计 4.广告元素 5.3d角色&…

Spring Boot 项目中使用 OpenAI ChatGPT APIs

大家好&#xff0c;我是Yuan&#xff0c;这篇文章主要介绍如何在springboot项目中集成调用chatgpt的api功能。 1. 概述 在本教程中&#xff0c;我们将学习如何在 Spring Boot 中调用 OpenAI ChatGPT API。我们将创建一个 Spring Boot 应用程序&#xff0c;该应用程序将通过调…

2021年茶艺师(中级)最新解析及茶艺师(中级)免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通&#xff1a;茶艺师&#xff08;中级&#xff09;最新解析考前必练&#xff01;安全生产模拟考试一点通每个月更新茶艺师&#xff08;中级&#xff09;免费试题题目及答案&#xff01;多做几遍…

2021年茶艺师(中级)最新解析及茶艺师(中级)模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通&#xff1a;茶艺师&#xff08;中级&#xff09;最新解析是安全生产模拟考试一点通生成的&#xff0c;茶艺师&#xff08;中级&#xff09;证模拟考试题库是根据茶艺师&#xff08;中级&#…

2021年茶艺师(中级)考试题库及茶艺师(中级)复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 茶艺师&#xff08;中级&#xff09;考试题库考前必练&#xff01;安全生产模拟考试一点通每个月更新茶艺师&#xff08;中级&#xff09;复审考试题目及答案&#xff01;多做几遍&#xff0c;其实通过茶艺师&#xf…