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?: {