Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/core/src/components/Bubble/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const props = withDefaults(defineProps<BubbleProps>(), {
avatarSrcSet: '',
avatarAlt: '',
avatarFit: 'cover',
noStyle: false
noStyle: false,
renderer: undefined
});

const emits = defineEmits<BubbleEmits>();
Expand Down Expand Up @@ -191,8 +192,7 @@ defineExpose(instance);
ref="typewriterRef"
:typing="_typing"
:content="content"
:is-markdown="isMarkdown"
:is-fog="props.isFog"
:renderer="renderer"
@start="onStart"
@writing="onWriting"
@finish="onFinish"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/Bubble/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { TypewriterProps } from '../Typewriter/types';

export type BubbleProps = Pick<
TypewriterProps,
'isFog' | 'typing' | 'content' | 'isMarkdown'
'renderer' | 'typing' | 'content'
> & {
placement?: 'start' | 'end';
avatar?: string;
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/components/BubbleList/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,7 @@ defineExpose({
:loading="item.loading"
:shape="item.shape"
:variant="item.variant"
:is-markdown="item.isMarkdown"
:is-fog="item.isFog"
:renderer="item.renderer"
:typing="item.typing"
:max-width="item.maxWidth"
:avatar="item.avatar"
Expand Down
81 changes: 24 additions & 57 deletions packages/core/src/components/Typewriter/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,15 @@ import type {
TypewriterProps,
TypingConfig
} from './types.d.ts';
import DOMPurify from 'dompurify'; // 新增安全过滤
import { useConfigProvider } from '../ConfigProvider/hooks.ts';

const props = withDefaults(defineProps<TypewriterProps>(), {
content: '',
isMarkdown: false,
typing: false,
isFog: false
renderer: undefined
});
const emits = defineEmits<TypewriterEmits>();

const configProvider = useConfigProvider();
const { md } = configProvider;

const markdownContentRef = ref<HTMLElement | null>(null);
const typeWriterRef = ref<HTMLElement | null>(null);

function initMarkdownPlugins() {
if (configProvider.mdPlugins?.length) {
configProvider.mdPlugins.forEach(plugin => {
md?.use(plugin);
});
}
if (props.mdPlugins?.length) {
props.mdPlugins.forEach(plugin => {
md?.use(plugin);
});
}
}
initMarkdownPlugins();

const typingIndex = ref(0);
const isTyping = ref(false);
let timer: ReturnType<typeof setTimeout> | null = null;
Expand All @@ -49,11 +27,8 @@ const mergedConfig: ComputedRef<TypingConfig> = computed(() => {
interval:
typeof props.typing === 'object' ? (props.typing.interval ?? 50) : 50,
// 根据条件动态设置后缀
suffix: props.isMarkdown
? ''
: typeof props.typing === 'object'
? (props.typing.suffix ?? '|')
: '|'
suffix:
typeof props.typing === 'object' ? (props.typing.suffix ?? '|') : '|'
};

// 处理打字配置
Expand All @@ -68,7 +43,7 @@ const mergedConfig: ComputedRef<TypingConfig> = computed(() => {
...defaultConfig,
...props.typing,
// 强制覆盖后缀设置
suffix: props.isMarkdown ? '' : (props.typing.suffix ?? '|')
suffix: props.typing.suffix ?? '|'
};
}

Expand All @@ -95,12 +70,7 @@ const typingProgress = computed(() => {
});
// 修改渲染内容计算属性
const renderedContent = computed(() => {
// 非Markdown模式直接返回
if (!props.isMarkdown) {
return processedContent.value;
}
// Markdown模式添加安全过滤和样式类
return DOMPurify.sanitize(md?.render(processedContent.value ?? '') ?? '');
return processedContent.value;
});

const instance: TypewriterInstance = {
Expand Down Expand Up @@ -203,35 +173,32 @@ defineExpose(instance);

<template>
<div ref="typeWriterRef" class="typer-container">
<component
:is="renderer"
v-if="renderer"
:markdown="renderedContent"
:is-typing="isTyping"
:progress="typingProgress"
:suffix="mergedConfig.suffix"
>
<!-- 可根据需要传递默认插槽内容 -->
</component>
<!-- 如果没有renderer,则渲染默认内容 -->
<div
ref="markdownContentRef"
v-else
class="typer-content"
:class="[
{
'markdown-content': isMarkdown,
'typing-cursor': typing && mergedConfig.suffix && isTyping,
'typing-cursor-foggy':
props.isFog && typing && mergedConfig.suffix && isTyping,
'typing-markdown-cursor-foggy':
isMarkdown && props.isFog && typing && isTyping
},
isMarkdown ? 'markdown-body' : ''
'typing-cursor': typing && mergedConfig.suffix && isTyping
}
]"
:style="{
'--cursor-char': `'${mergedConfig.suffix}'`,
'--cursor-fog-bg-color': props.isFog
? typeof props.isFog === 'object'
? (props.isFog.bgColor ?? 'var(--el-fill-color)')
: 'var(--el-fill-color)'
: '',
'--cursor-fog-width': props.isFog
? typeof props.isFog === 'object'
? (props.isFog.width ?? '80px')
: '80px'
: ''
'--cursor-char': `'${mergedConfig.suffix}'`
}"
v-html="renderedContent"
/>
>
<!-- 显示打字机渲染内容 -->
{{ renderedContent }}
</div>
</div>
</template>

Expand Down
10 changes: 2 additions & 8 deletions packages/core/src/components/Typewriter/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type MarkdownIt from 'markdown-it';
import type { ComputedRef, Ref } from 'vue';
import type { Component, ComputedRef, Ref } from 'vue';

export interface TypingConfig {
step?: number;
Expand All @@ -12,15 +11,10 @@ export interface TypingFogfig {
width?: string;
}

type MarkdownItPlugin = (md: MarkdownIt) => void;

export interface TypewriterProps {
renderer?: Component;
content?: string;
isMarkdown?: boolean;
typing?: boolean | TypingConfig;
isFog?: boolean | TypingFogfig;
highlight?: (code: string, language: string) => string;
mdPlugins?: MarkdownItPlugin[];
}

export interface TypewriterEmits {
Expand Down
14 changes: 8 additions & 6 deletions packages/core/src/stories/Bubble/Bubble.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type BubbleSource from '@components/Bubble/index.vue';
import type { Meta, StoryObj } from '@storybook/vue3';
import { avatar1, mdContent } from '@assets/mock';
import { XMarkdown } from '../../components';
import CustomSolt from './CustomSolt.vue';
import Bubble from './index.vue';

Expand All @@ -10,9 +11,9 @@ const meta = {
tags: ['autodocs'],
argTypes: {
content: { control: 'text' },
isMarkdown: { control: 'boolean' },
// isMarkdown: { control: 'boolean' },
typing: { control: 'object' },
isFog: { control: 'boolean' },
// isFog: { control: 'boolean' },
// 气泡属性
placement: { control: 'radio', options: ['start', 'end'] },
avatar: { control: 'text' },
Expand All @@ -38,14 +39,15 @@ const meta = {
avatar: avatar1,
loading: false,
content: mdContent,
isMarkdown: true,
renderer: markRaw(XMarkdown),
// isMarkdown: true,
typing: {
step: 2,
suffix: '💗',
interval: 100,
isRequestEnd: true
},
isFog: true,
// isFog: true,
placement: 'start',
shape: 'round',
variant: 'filled',
Expand All @@ -71,8 +73,8 @@ export const BubbleDemo: Story = {
export const SoltDemo: Story = {
args: {
...BubbleDemo.args,
content: '欢迎使用 Element Plus X',
ismarkdown: false
content: '欢迎使用 Element Plus X'
// ismarkdown: false
} as Story['args'],
render: (args: any) => ({
components: {
Expand Down
51 changes: 26 additions & 25 deletions packages/core/src/stories/Bubble/index.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
<script setup lang="ts">
import Bubble from '@components/Bubble/index.vue';
import ConfigProvider from '@components/ConfigProvider/index.vue';
import markdownItMermaid from '@jsonlee_12138/markdown-it-mermaid';
// import ConfigProvider from '@components/ConfigProvider/index.vue';
// import markdownItMermaid from '@jsonlee_12138/markdown-it-mermaid';

const mdPlugins = [
markdownItMermaid({
forceLegacyMathML: true,
delay: 100
})
];
// const mdPlugins = [
// markdownItMermaid({
// forceLegacyMathML: true,
// delay: 100
// })
// ];
</script>

<template>
<ConfigProvider :md-plugins="mdPlugins">
<div class="component-container">
<p>
1.2.0 版本支持 打字器 雾化效果 使用 Mermaid.js 支持简单的图表和函数公式
</p>
<p style="color: #ff8c00">
在这个版本的 md 我们将 markdown-it
配置全部暴露出来了,需要大家自行集成配置,包括代码高亮和简单的图表、函数公式这些。
</p>
<p style="color: #f00">
后面可能会上一个大的版本,找到更好的处理 md
渲染的方法。大家有好的想法可以加交流群或者作者VX一起交流
</p>
<div class="component-1">
<Bubble v-bind="$attrs" />
</div>
<!-- <ConfigProvider :md-plugins="mdPlugins"> -->
<div class="component-container">
<b>雾化效果和markdown内容已去除,支持传入renderer自定义渲染器</b>
<p>
1.2.0 版本支持 打字器 雾化效果 使用 Mermaid.js 支持简单的图表和函数公式
</p>
<p style="color: #ff8c00">
在这个版本的 md 我们将 markdown-it
配置全部暴露出来了,需要大家自行集成配置,包括代码高亮和简单的图表、函数公式这些。
</p>
<p style="color: #f00">
后面可能会上一个大的版本,找到更好的处理 md
渲染的方法。大家有好的想法可以加交流群或者作者VX一起交流
</p>
<div class="component-1">
<Bubble v-bind="$attrs" />
</div>
</ConfigProvider>
</div>
<!-- </ConfigProvider> -->
</template>

<style scoped lang="scss">
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/stories/BubbleList/CustomSolt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function addMessage() {
content,
placement,
typing,
isFog: true,
// isFog: true,
avatar: isUser ? avatar1 : avatar2,
avatarSize: '32px'
};
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/stories/BubbleList/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function addMessage() {
content,
placement,
typing,
isFog: true,
// isFog: true,
avatar: isUser ? avatar1 : avatar2,
avatarSize: '32px'
};
Expand Down
21 changes: 12 additions & 9 deletions packages/core/src/stories/Typewriter/Typewriter.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
mdContent
// mermaidMdContent
} from '@assets/mock';
import { XMarkdown } from '../../components';
// import PrismDemo from './CustomPrismDemo.vue';
// import ShikiDemo from './CustomShikiDemo.vue';
import Typewriter from './index.vue';
Expand All @@ -19,19 +20,19 @@ const meta = {
tags: ['autodocs'],
argTypes: {
content: { control: 'text' },
isMarkdown: { control: 'boolean' },
typing: { control: 'object' },
isFog: { control: 'boolean' }
// isMarkdown: { control: 'boolean' },
typing: { control: 'object' }
// isFog: { control: 'boolean' }
},
args: {
typing: {
step: 2,
interval: 100,
suffix: '|',
isRequestEnd: true
},
isFog: true,
isMarkdown: true
}
// isFog: true,
// isMarkdown: true
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
}
} satisfies Meta<typeof TypewriterSource>;
Expand All @@ -46,13 +47,15 @@ type Story = StoryObj<typeof meta>;
export const TypewriterDemo: Story = {
args: {
content: mdContent,
isFog: true,
isMarkdown: true
renderer: markRaw(XMarkdown)
// isFog: true,
// isMarkdown: true
} as Story['args']
};

export const MathRenderDemo: Story = {
args: {
content: mathMdContent
content: mathMdContent,
renderer: markRaw(XMarkdown)
} as Story['args']
};
Loading