diff --git a/examples/api.html b/examples/api.html index 1316cec2..8f6e023f 100644 --- a/examples/api.html +++ b/examples/api.html @@ -281,6 +281,16 @@

refreshPreviewer()

+
+

clearFlowSessionCursor()

+

清空流程会话中添加的虚拟光标

+
+ + 试一试 +
+
+

diff --git a/examples/scripts/ai-chat-demo.js b/examples/scripts/ai-chat-demo.js index 0ec578b0..736552a9 100644 --- a/examples/scripts/ai-chat-demo.js +++ b/examples/scripts/ai-chat-demo.js @@ -7,6 +7,7 @@ var cherryConfig = { global: { // 开启流式模式 (默认 true) flowSessionContext: true, + flowSessionCursor: 'default', }, syntax: { codeBlock: { diff --git a/src/Cherry.config.js b/src/Cherry.config.js index 10d2b52c..043c1f46 100644 --- a/src/Cherry.config.js +++ b/src/Cherry.config.js @@ -197,6 +197,13 @@ const defaultConfig = { * 后续如果有新的需求,可提issue反馈 */ flowSessionContext: true, + /** + * 流式会话时,在最后位置增加一个类似光标的dom + * - 'default':用cherry提供的默认样式 + * - '':不增加任何dom + * - '': 自定义的dom + */ + flowSessionCursor: '', }, // 内置语法配置 syntax: { diff --git a/src/Cherry.js b/src/Cherry.js index c114bcca..2fdb39eb 100644 --- a/src/Cherry.js +++ b/src/Cherry.js @@ -122,6 +122,9 @@ export default class Cherry extends CherryStatic { this.lastMarkdownText = ''; this.$event = new Event(this.instanceId); + if (this.options.engine.global.flowSessionCursor === 'default') { + this.options.engine.global.flowSessionCursor = ''; + } /** * @type {import('./Engine').default} */ @@ -1086,4 +1089,16 @@ export default class Cherry extends CherryStatic { // @ts-ignore this.toc.setModelToLocalStorage(targetModel); } + + /** + * 清空流程会话中添加的虚拟光标 + */ + clearFlowSessionCursor() { + if (this.options.engine.global.flowSessionCursor) { + this.previewer.getDom().innerHTML = this.previewer + .getDom() + // @ts-ignore + .innerHTML.replaceAll(this.options.engine.global.flowSessionCursor, ''); + } + } } diff --git a/src/Engine.js b/src/Engine.js index d66eb466..9969de26 100644 --- a/src/Engine.js +++ b/src/Engine.js @@ -266,17 +266,49 @@ export default class Engine { }); } + /** + * 流式输出场景时,在最后增加一个光标占位 + * @param {string} md 内容 + * @returns {string} + */ + $setFlowSessionCursorCache(md) { + if (this.$cherry.options.engine.global.flowSessionContext && this.$cherry.options.engine.global.flowSessionCursor) { + return `${md}CHERRY_FLOW_SESSION_CURSOR`; + } + return md; + } + + /** + * 流式输出场景时,把最后的光标占位替换为配置的dom元素,并在一段时间后删除该元素 + * @param {string} md 内容 + * @returns {string} + */ + $clearFlowSessionCursorCache(md) { + if (this.$cherry.options.engine.global.flowSessionCursor) { + if (this.clearCursorTimer) { + clearTimeout(this.clearCursorTimer); + } + this.clearCursorTimer = setTimeout(() => { + this.$cherry.clearFlowSessionCursor(); + }, 2560); + return md.replace(/CHERRY_FLOW_SESSION_CURSOR/g, this.$cherry.options.engine.global.flowSessionCursor); + } + return md; + } + /** * @param {string} md md字符串 * @returns {string} 获取html */ makeHtml(md) { - let $md = this.$cacheBigData(md); + let $md = this.$setFlowSessionCursorCache(md); + $md = this.$cacheBigData($md); $md = this.$beforeMakeHtml($md); $md = this.$dealParagraph($md); $md = this.$afterMakeHtml($md); this.$fireHookAction($md, 'paragraph', '$cleanCache'); $md = this.$deCacheBigData($md); + $md = this.$clearFlowSessionCursorCache($md); return $md; } diff --git a/src/sass/cherry.scss b/src/sass/cherry.scss index 7b8c3916..5b5b5e67 100644 --- a/src/sass/cherry.scss +++ b/src/sass/cherry.scss @@ -813,6 +813,24 @@ background-color: #3582fb; } } + + @keyframes blink { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + .cherry-flow-session-cursor { + background-color: #3582fb88; + padding: 0 2.5px; + animation: blink 1s infinite; + } } .cherry-color-wrap { diff --git a/types/cherry.d.ts b/types/cherry.d.ts index 1d94c81b..09400b24 100644 --- a/types/cherry.d.ts +++ b/types/cherry.d.ts @@ -237,6 +237,13 @@ export interface CherryEngineOptions { * 后续如果有新的需求,可提issue反馈 */ flowSessionContext?: boolean; + /** + * 流式会话时,在最后位置增加一个类似光标的dom + * - 'default':用cherry提供的默认样式 + * - '':不增加任何dom + * - '': 自定义的dom + */ + flowSessionCursor?: string; }; /** 内置语法配置 */ syntax?: {