无线蓝牙耳机 Pro
-主动降噪技术,30小时续航,IPX5防水等级
- - - -
- ¥499
- ¥699
- 7折
-
-
-
-
-
-
-
-
- diff --git a/Yi.Ai.Vue3/.claude/settings.local.json b/Yi.Ai.Vue3/.claude/settings.local.json index 776c8df4..2379a8d2 100644 --- a/Yi.Ai.Vue3/.claude/settings.local.json +++ b/Yi.Ai.Vue3/.claude/settings.local.json @@ -6,7 +6,9 @@ "Bash(npm run dev:*)", "Bash(taskkill:*)", "Bash(timeout /t 5 /nobreak)", - "Bash(git checkout:*)" + "Bash(git checkout:*)", + "Bash(npm install marked --save)", + "Bash(pnpm add marked)" ], "deny": [], "ask": [] diff --git a/Yi.Ai.Vue3/index.html b/Yi.Ai.Vue3/index.html index 51fe52dc..70c5ca53 100644 --- a/Yi.Ai.Vue3/index.html +++ b/Yi.Ai.Vue3/index.html @@ -112,7 +112,7 @@
主动降噪技术,30小时续航,IPX5防水等级
- - - -
-
-public class HelloWorld {
- public static void main(String[] args) {
- System.out.println("Hello, world!");
- }
-}
-
-
-\`\`\`echarts
-use codeXRender for echarts render
-\`\`\`
-### javascript
-\`\`\`javascript
-console.log('Hello, world!');
-\`\`\`
-### java
-\`\`\`java
-public class HelloWorld {
- public static void main(String[] args) {
- System.out.println("Hello, world!");
- }
-}
-\`\`\`
-\`\`\`typescript
-import {
- ArrowDownBold,
- CopyDocument,
- Moon,
- Sunny
-} from '@element-plus/icons-vue';
-import { ElButton, ElSpace } from 'element-plus';
-import { h } from 'vue';
-
-/* ----------------------------------- 按钮组 ---------------------------------- */
-
-/**
- * @description 描述 language标签
- * @date 2025-06-25 17:48:15
- * @author tingfeng
- *
- * @export
- * @param language
- */
-export function languageEle(language: string) {
- return h(
- ElSpace,
- {},
- {}
- );
-}
-\`\`\`
-`.trim();
-
-// md 美人鱼图表
-export const mermaidMdContent = `
-
-### mermaid 饼状图
-\`\`\`mermaid
-pie
- "传媒及文化相关" : 35
- "广告与市场营销" : 8
- "游戏开发" : 15
- "影视动画与特效" : 12
- "互联网产品设计" : 10
- "VR/AR开发" : 5
- "其他" : 15
-\`\`\`
-
-`;
-
-// md 数学公式
-export const mathMdContent = `
-### mermaid 流程图
-\`\`\`mermaid
-graph LR
- 1 --> 2
- 2 --> 3
- 3 --> 4
- 2 --> 1
- 2-3 --> 1-3
-\`\`\`
-\`\`\`mermaid
-flowchart TD
- Start[开始] --> Check[是否通过?]
- Check -- 是 --> Pass[流程继续]
- Check -- 否 --> Reject[流程结束]
-\`\`\`
-\`\`\`mermaid
-flowchart TD
- %% 前端专项四层结构
- A["战略层
- 【提升用户体验】"]
- --> B["架构层
- 【微前端方案选型】"]
- --> C["框架层
- 【React+TS技术栈】"]
- --> D["实现层
- 【组件库开发】"]
- style A fill:#FFD700,stroke:#FFA500
- style B fill:#87CEFA,stroke:#1E90FF
- style C fill:#9370DB,stroke:#663399
- style D fill:#FF6347,stroke:#CD5C5C
-
-\`\`\`
-### mermaid 数学公式
-\`\`\`mermaid
-sequenceDiagram
- autonumber
- participant 1 as $$alpha$$
- participant 2 as $$beta$$
- 1->>2: Solve: $$\sqrt{2+2}$$
- 2-->>1: Answer: $$2$$
-\`\`\`
-
-`;
-export const customAttrContent = `
-element-plus-x
-
-
-
-
- {{
- props.raw.content
- }}
-
- -------
instead for customizing fromnot? - case 'code': - vnodeProps.languageOriginal = Array.isArray(attrs.class) - ? attrs.class.find(cls => cls.startsWith('language-')) - : ''; - vnodeProps.language = vnodeProps.languageOriginal - ? vnodeProps.languageOriginal.replace('language-', '') - : ''; - vnodeProps.inline = 'tagName' in parent && parent.tagName !== 'pre'; - - // when tagName is code, it definitely has children and the first child is text - // https://github.com/syntax-tree/mdast-util-to-hast/blob/main/lib/handlers/code.js - vnodeProps.content = (node.children[0] as unknown as Text)?.value ?? ''; - - aliasList.push(vnodeProps.inline ? 'inline-code' : 'block-code'); - break; - case 'thead': - case 'tbody': - ctx.currentContext = node.tagName; - break; - case 'td': - case 'th': - case 'tr': - vnodeProps.isHead = context.currentContext === 'thead'; - break; - - case 'ul': - case 'ol': - ctx.listDepth = context.listDepth + 1; - ctx.listOrdered = node.tagName === 'ol'; - ctx.listItemIndex = -1; - vnodeProps.ordered = ctx.listOrdered; - vnodeProps.depth = ctx.listDepth; - - aliasList.push('list'); - break; - - case 'li': - ctx.listItemIndex++; - - vnodeProps.ordered = ctx.listOrdered; - vnodeProps.depth = ctx.listDepth; - vnodeProps.index = ctx.listItemIndex; - aliasList.push('list-item'); - - break; - case 'slot': - if (typeof node.properties['slot-name'] === 'string') { - aliasList.push(`${node.properties['slot-name']}`); - delete node.properties['slot-name']; - } - break; - default: - break; - } - - attrs = computeAttrs( - node, - aliasList, - vnodeProps, - { ...attrs } as Attributes, // TODO: fix this - customAttrs - ); - } - - return { - attrs, - context: ctx, - aliasList, - vnodeProps - }; -} - -/** - * TODO: - * @param node - hast node - * @param aliasList - html tag list. The earlier alias has higher priority. ? - * @param attrs - attrs - * @param customAttrs - custom attrs object - * @returns attrs - */ -function computeAttrs( - node: Element, - aliasList: AliasList, - vnodeProps: Record, - attrs: Attributes, - customAttrs: CustomAttrs -): CustomAttrsObjectResult { - const result: CustomAttrsObjectResult = { - ...attrs - }; - for (let i = aliasList.length - 1; i >= 0; i--) { - const name = aliasList[i]; - // console.log(Object.keys(customAttrs)) - if (name in customAttrs) { - const customAttr = customAttrs[name]; - return { - ...result, - ...(typeof customAttr === 'function' - ? customAttr(node, { ...attrs, ...vnodeProps }) - : customAttr) - }; - } - } - return result; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/index.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/index.ts deleted file mode 100644 index 9a30ba85..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -// shunnNet has the rights under the MIT license -export { VueMarkdown, VueMarkdownAsync } from './components'; -export { getVNodeInfos, render, renderChildren } from './hast-to-vnode'; -export type * from './types'; -export { createProcessor, useMarkdownProcessor } from './useProcessor'; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/types.d.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/types.d.ts deleted file mode 100644 index 19a481c6..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/types.d.ts +++ /dev/null @@ -1,389 +0,0 @@ -import type { Options as DeepMergeOptions } from 'deepmerge'; -import type { Element } from 'hast'; -import type { Options as TRehypeOptions } from 'mdast-util-to-hast'; -import type { Options } from 'rehype-sanitize'; -import type { PluggableList } from 'unified'; -import type { - AllowedComponentProps, - ComponentCustomProps, - VNode, - VNodeArrayChildren, - VNodeProps -} from 'vue'; - -export interface Context { - listDepth: number; - listOrdered: boolean; - listItemIndex: number; - currentContext?: string; - svg: boolean; -} -export type Attributes = Record ; - -interface TTableProps { - /** whether it is in head */ - isHead: boolean; -} - -interface THeadingProps { - /** heading level */ - level: number; -} - -interface TListProps { - /** depth of the list */ - depth: number; - /** whether it is ordered list */ - ordered: boolean; -} - -interface TCodeProps { - /** language name original @example 'language-js' */ - languageOriginal: string; - - /** language name @example 'js' */ - language: string; - - /** code content */ - content: string; - - /** whether it is inline code */ - inline: boolean; -} - -// https://www.google.com/search?q=record%3Cstring,+any%3E+vs+record%3Cstring,+unknown%3E&sourceid=chrome&ie=UTF-8 -export type CustomAttrsObjectResult = Record ; - -type CustomAttrsFunctionValue = ( - /** - * hast node - * - * Please refer to the source code at the following URL to understand the possible attributes for each element. - * - * @see https://github.com/syntax-tree/mdast-util-to-hast/tree/main/lib/handlers - */ - node: Element, - /** - * Properties of the current element. - * - * Except for the basic properties provided from hast, it also includes custom properties such as `level`, `ordered`, `depth`, `index` etc. - */ - combinedAttrs: T | Attributes -) => Record ; - -type CustomAttrsValue< - T extends Record = Record -> = CustomAttrsObjectResult | CustomAttrsFunctionValue ; - -type TBasicHTMLTagNames = keyof Omit< - HTMLElementTagNameMap, - | 'h1' - | 'h2' - | 'h3' - | 'h4' - | 'h5' - | 'h6' - | 'ul' - | 'ol' - | 'li' - | 'code' - | 'td' - | 'th' - | 'tr' ->; -export type CustomAttrs = { - [key in TBasicHTMLTagNames]?: CustomAttrsValue; // << dynamic properties -} & { - [key: string]: - | CustomAttrsValue - | CustomAttrsValue - | CustomAttrsValue - | CustomAttrsValue - | CustomAttrsValue - | undefined; - ['h1']?: CustomAttrsValue ; // << static properties - ['h2']?: CustomAttrsValue ; - ['h3']?: CustomAttrsValue ; - ['h4']?: CustomAttrsValue ; - ['h5']?: CustomAttrsValue ; - ['h6']?: CustomAttrsValue ; - ['heading']?: CustomAttrsValue ; - ['ul']?: CustomAttrsValue ; - ['ol']?: CustomAttrsValue ; - ['list']?: CustomAttrsValue ; - ['li']?: CustomAttrsValue ; - ['list-item']?: CustomAttrsValue ; - ['code']?: CustomAttrsValue ; - ['inline-code']?: CustomAttrsValue ; - ['block-code']?: CustomAttrsValue ; - ['td']?: CustomAttrsValue ; - ['th']?: CustomAttrsValue ; - ['tr']?: CustomAttrsValue ; -}; - -export type AliasList = string[]; -export type TagList = AliasList; - -export interface SanitizeOptions { - /** - * Options for `rehype-sanitize` - * - * @see https://github.com/rehypejs/rehype-sanitize - */ - sanitizeOptions?: Options; - /** - * Options for `deepmerge` - */ - mergeOptions?: DeepMergeOptions; -} - -export interface TVueMarkdownProps { - /** - * Markdown content - * - * @default ''' - */ - markdown: string; - /** - * You can set custom attributes for each element, such as `href`, `target`, `rel`, `lazyload`, etc. - * - * The key is the HTML tag name, and the value can either be an object or a function that returns an object. - * - * The value will be passed to Vue's `h` function. You can refer to Vue's official documentation to learn how to configure `h`. - * - * @see https://vuejs.org/guide/extras/render-function.html#render-functions-jsx - * - * @default {} - * - * @example - * ```ts - * { - * a: { target: '_blank', rel: 'noopener' }, - * img: { lazyload: true }, - * h1: (node, combinedAttrs) => { - * return { class: ['title', 'mb-2'] } - * } - * } - * ``` - */ - customAttrs?: CustomAttrs; - /** - * Remark plugins - * - * These plugins will be used between `remark-parse` and `remark-rehype`. - * - * @see https://github.com/remarkjs/remark?tab=readme-ov-file#plugins - * - * @default [] - */ - remarkPlugins?: PluggableList; - /** - * rehype plugins - * - * These plugins will be used after `remark-rehype` but before `rehype-sanitize`. - * - * @see https://github.com/remarkjs/remark-rehype?tab=readme-ov-file#related - * - * @default [] - */ - rehypePlugins?: PluggableList; - /** - * Whether to sanitize the HTML content. (use `rehype-sanitize`) - * - * You need disable this option if you want to render ` ` in markdown content. - * - * @default false - */ - sanitize?: boolean; - /** - * Options for `rehype-sanitize` - * - * @see https://github.com/rehypejs/rehype-sanitize?tab=readme-ov-file#options - * - * @default { allowDangerousHtml: true } - */ - sanitizeOptions?: SanitizeOptions; - - /** - * Options for `rehype-parse` - * - * @see https://github.com/remarkjs/remark-rehype?tab=readme-ov-file#options - * - * @default {} - */ - rehypeOptions?: Omit ; -} - -/** - * Typed version of the `VueMarkdown` component. - * - * Copy from vue-router - */ -export interface TVueMarkdown { - new (): { - $props: AllowedComponentProps & - ComponentCustomProps & - VNodeProps & - TVueMarkdownProps; - - $slots: TBaseSlots & { - /** - * Customize ` ` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h1']?: THeadingSlot; - /** - * Customize `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h2']?: THeadingSlot; - /** - * Customize `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h3']?: THeadingSlot; - /** - * Customize `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h4']?: THeadingSlot; - /** - * Customize `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h5']?: THeadingSlot; - /** - * Customize `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['h6']?: THeadingSlot; - /** - * Customize `
` ~ `
` tag - * @scope `level` heading level - * @scope `children` Functional component, child elements. - */ - ['heading']?: THeadingSlot; - - /** - * Customize inline and block code. - * @scope `languageOriginal` language name original - * @scope `language` language name - * @scope `content` code content - * @scope `inline` whether it is inline code - * @scope `children` Functional component, child elements. - */ - ['code']?: TCodeSlot; - /** - * Customize inline code. - * @scope `languageOriginal` language name original - * @scope `language` language name - * @scope `content` code content - * @scope `inline` whether it is inline code - * @scope `children` Functional component, child elements. - */ - ['inline-code']?: TCodeSlot; - /** - * Customize block code. - * @scope `languageOriginal` language name original - * @scope `language` language name - * @scope `content` code content - * @scope `inline` whether it is inline code - * @scope `children` Functional component, child elements. - */ - ['block-code']?: TCodeSlot; - - /** - * Customize unordered list - * @scope `depth` depth of the list - * @scope `ordered` whether it is ordered list - * @scope `children` Functional component, child elements. - */ - ['ul']?: TListSlot; - - /** - * Customize ordered list - * @scope `depth` depth of the list - * @scope `ordered` whether it is ordered list - * @scope `children` Functional component, child elements. - */ - ['ol']?: TListSlot; - /** - * Customize ordered and unordered list - * @scope `depth` depth of the list - * @scope `ordered` whether it is ordered list - * @scope `children` Functional component, child elements. - */ - ['list']?: TListSlot; - - /** - * Customize list item - * @scope `depth` depth of the list - * @scope `ordered` whether it is ordered list - * @scope `index` index of the list item - * @scope `children` Functional component, child elements. - */ - ['li']?: TListSlot; - - /** - * Customize list item - * @scope `depth` depth of the list - * @scope `ordered` whether it is ordered list - * @scope `index` index of the list item - * @scope `children` Functional component, child elements. - */ - ['list-item']?: TListSlot; - - /** - * Customize table element: td - * - * @scope `isHead` whether it is in head - * @scope `children` Functional component, child elements. - */ - ['td']?: TTableElementSlot; - - /** - * Customize table element: th - * - * @scope `isHead` whether it is in head - * @scope `children` Functional component, child elements. - */ - ['th']?: TTableElementSlot; - - /** - * Customize table element: tr - * - * @scope `isHead` whether it is in head - * @scope `children` Functional component, child elements. - */ - ['tr']?: TTableElementSlot; - }; - }; -} - -type TTableElementSlot = TCustomSlot
; -type TListSlot = TCustomSlot ; -type THeadingSlot = TCustomSlot ; -type TCodeSlot = TCustomSlot ; - -type HtmlTagNames = keyof HTMLElementTagNameMap; -type TBaseSlots = { - // An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.ts(1337) - // [key: HtmlTagNames]: (scope: Record ) => VNode[] | VNode - [key in HtmlTagNames]?: (scope: TBaseSlotScope) => VNode[] | VNode; -} & { - [key: string]: (scope: TBaseSlotScope) => VNode[] | VNode; -}; - -type TBaseSlotScope = TElementChild & Attributes; -interface TElementChild { - /** Functional component, child elements. */ - children: () => VNodeArrayChildren; -} - -type TCustomSlot = (scope: TBaseSlotScope & T) => VNode[] | VNode; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/useProcessor.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/useProcessor.ts deleted file mode 100644 index ef06e340..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/useProcessor.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Root } from 'hast'; -import type { Root as MdastRoot } from 'mdast'; -import type { Options as TRehypeOptions } from 'mdast-util-to-hast'; -import type { PluggableList, Processor } from 'unified'; -import type { ComputedRef, MaybeRefOrGetter } from 'vue'; -import type { SanitizeOptions } from './types'; -import deepmerge from 'deepmerge'; -import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; -import remarkParse from 'remark-parse'; -import remarkRehype from 'remark-rehype'; -import { unified } from 'unified'; -import { computed, toValue } from 'vue'; - -export interface TUseMarkdownProcessorOptions { - remarkPlugins?: MaybeRefOrGetter ; - rehypePlugins?: MaybeRefOrGetter ; - rehypeOptions?: MaybeRefOrGetter >; - sanitize?: MaybeRefOrGetter ; - sanitizeOptions?: MaybeRefOrGetter ; -} -export function useMarkdownProcessor(options?: TUseMarkdownProcessorOptions): { - processor: ComputedRef< - Processor - >; -} { - const processor = computed(() => { - return createProcessor({ - prePlugins: [remarkParse, ...(toValue(options?.remarkPlugins) ?? [])], - rehypePlugins: toValue(options?.rehypePlugins), - rehypeOptions: toValue(options?.rehypeOptions), - sanitize: toValue(options?.sanitize), - sanitizeOptions: toValue(options?.sanitizeOptions) - }); - }); - return { processor }; -} - -export function createProcessor(options?: { - prePlugins?: PluggableList; - rehypePlugins?: PluggableList; - rehypeOptions?: Omit ; - sanitize?: boolean; - sanitizeOptions?: SanitizeOptions; - // TODO: fix types -}): Processor { - return unified() - .use(options?.prePlugins ?? []) - .use(remarkRehype, { - allowDangerousHtml: true, - ...(options?.rehypeOptions || {}) - }) - .use(options?.rehypePlugins ?? []) - .use( - options?.sanitize - ? [ - [ - rehypeSanitize, - deepmerge( - defaultSchema, - options?.sanitizeOptions?.sanitizeOptions || {}, - options?.sanitizeOptions?.mergeOptions || {} - ) - ] - ] - : [] - ); -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/index.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/index.ts deleted file mode 100644 index 658cb4a0..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './useComponents'; -export * from './useMarkdown'; -export * from './useMermaid'; -export * from './useMermaidZoom'; -export * from './usePlugins'; -export * from './useThemeMode'; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useComponents.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useComponents.ts deleted file mode 100644 index 4ae3e81a..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useComponents.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { h } from 'vue'; -import { CodeX } from '../components/index'; - -function useComponents() { - const components = { - code: (raw: any) => h(CodeX, { raw }) - }; - return components; -} - -export { useComponents }; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMarkdown.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMarkdown.ts deleted file mode 100644 index 57c912fd..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMarkdown.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { flow } from 'lodash-es'; - -export function useProcessMarkdown(markdown: string) { - return preprocessLaTeX(markdown); -} - -export function preprocessLaTeX(markdown: string) { - if (typeof markdown !== 'string') return markdown; - - const codeBlockRegex = /```[\s\S]*?```/g; - const codeBlocks = markdown.match(codeBlockRegex) || []; - const escapeReplacement = (str: string) => str.replace(/\$/g, '_ELX_DOLLAR_'); - let processedMarkdown = markdown.replace( - codeBlockRegex, - 'ELX_CODE_BLOCK_PLACEHOLDER' - ); - - processedMarkdown = flow([ - (str: string) => - str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`), - (str: string) => - str.replace(/\\\[([\s\S]*?)\\\]/g, (_, equation) => `$$${equation}$$`), - (str: string) => - str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`), - (str: string) => - str.replace( - /(^|[^\\])\$(.+?)\$/g, - (_, prefix, equation) => `${prefix}$${equation}$` - ) - ])(processedMarkdown); - - codeBlocks.forEach(block => { - processedMarkdown = processedMarkdown.replace( - 'ELX_CODE_BLOCK_PLACEHOLDER', - escapeReplacement(block) - ); - }); - - processedMarkdown = processedMarkdown.replace(/_ELX_DOLLAR_/g, '$'); - - return processedMarkdown; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaid.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaid.ts deleted file mode 100644 index 2086af6c..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaid.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { Ref } from 'vue'; -import { throttle } from 'lodash-es'; -import { computed, ref, watch } from 'vue'; - -interface UseMermaidOptions { - id?: string; - theme?: 'default' | 'dark' | 'forest' | 'neutral' | string; - config?: any; -} - -async function loadMermaid() { - if (typeof window === 'undefined') return null; - const mermaidModule = await import('mermaid'); - return mermaidModule.default; -} - -let mermaidContainer: HTMLElement | null = null; - -function getMermaidContainer(): HTMLElement { - if (!mermaidContainer) { - mermaidContainer = document.querySelector( - '.elx-markdown-mermaid-container' - ) as HTMLElement; - if (!mermaidContainer) { - mermaidContainer = document.createElement('div') as HTMLElement; - mermaidContainer.ariaHidden = 'true'; - mermaidContainer.style.maxHeight = '0'; - mermaidContainer.style.opacity = '0'; - mermaidContainer.style.overflow = 'hidden'; - mermaidContainer.classList.add('elx-markdown-mermaid-container'); - document.body.append(mermaidContainer); - } - } - return mermaidContainer; -} - -export function useMermaid( - content: string | Ref , - options: UseMermaidOptions = {} -) { - const { id = 'mermaid', theme = 'default', config = {} } = options; - const mermaidConfig = computed(() => ({ - suppressErrorRendering: true, - startOnLoad: false, - securityLevel: 'loose', - theme, - ...config - })); - const data = ref(''); - const error = ref (null); - const throttledRender = throttle( - async () => { - const contentValue = - typeof content === 'string' ? content : content.value; - if (!contentValue?.trim()) { - data.value = ''; - error.value = null; - return; - } - try { - // 动态加载 mermaid 库 - const mermaidInstance = await loadMermaid(); - if (!mermaidInstance) { - data.value = contentValue; - error.value = null; - return; - } - // 语法校验 - const isValid = await mermaidInstance.parse(contentValue.trim()); - if (!isValid) { - console.log('Mermaid parse error: Invalid syntax'); - data.value = ''; - error.value = new Error('Mermaid parse error: Invalid syntax'); - return; - } - // 初始化 mermaid 配置 - mermaidInstance.initialize(mermaidConfig.value); - const renderId = `${id}-${Math.random().toString(36).substr(2, 9)}`; - const container = getMermaidContainer(); - const { svg } = await mermaidInstance.render( - renderId, - contentValue, - container - ); - data.value = svg; - error.value = null; - } catch (err) { - console.log('Mermaid render error:', err); - data.value = ''; - error.value = err; - } - }, - 300, - { trailing: true, leading: true } - ); - - // 监听内容变化,自动触发渲染 - watch( - () => (typeof content === 'string' ? content : content.value), - () => { - throttledRender(); - }, - { immediate: true } - ); - - return { - data, - error - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaidZoom.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaidZoom.ts deleted file mode 100644 index 606ed7f9..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useMermaidZoom.ts +++ /dev/null @@ -1,179 +0,0 @@ -import type { - MermaidZoomControls, - UseMermaidZoomOptions -} from '../components/Mermaid/types'; -import { onUnmounted, ref, watch } from 'vue'; - -export function useMermaidZoom( - options: UseMermaidZoomOptions -): MermaidZoomControls { - const { container } = options; - - const scale = ref(1); - const posX = ref(0); - const posY = ref(0); - const isDragging = ref(false); - - let removeEvents: (() => void) | null = null; - - // 获取SVG元素 - const getSvg = () => - container.value?.querySelector('.mermaid-content svg') as HTMLElement; - - // 更新变换 - const updateTransform = (svg: HTMLElement) => { - svg.style.transformOrigin = 'center center'; - svg.style.transform = `translate(${posX.value}px, ${posY.value}px) scale(${scale.value})`; - }; - - // 重置状态 - const resetState = () => { - scale.value = 1; - posX.value = 0; - posY.value = 0; - isDragging.value = false; - }; - - // 添加拖拽事件 - const addDragEvents = (content: HTMLElement) => { - let startX = 0; - let startY = 0; - - const onStart = (clientX: number, clientY: number) => { - isDragging.value = true; - startX = clientX - posX.value; - startY = clientY - posY.value; - document.body.style.userSelect = 'none'; - }; - - const onMove = (clientX: number, clientY: number) => { - if (isDragging.value) { - posX.value = clientX - startX; - posY.value = clientY - startY; - updateTransform(content); - } - }; - - const onEnd = () => { - isDragging.value = false; - document.body.style.userSelect = ''; - }; - - // 鼠标事件 - const onMouseDown = (e: MouseEvent) => { - if (e.button !== 0) - return; // ⭐️ 只响应鼠标左键 - e.preventDefault(); - onStart(e.clientX, e.clientY); - }; - const onMouseMove = (e: MouseEvent) => onMove(e.clientX, e.clientY); - - // 触摸事件 - const onTouchStart = (e: TouchEvent) => { - if (e.touches.length === 1) { - onStart(e.touches[0].clientX, e.touches[0].clientY); - } - }; - const onTouchMove = (e: TouchEvent) => { - if (e.touches.length === 1) { - e.preventDefault(); - onMove(e.touches[0].clientX, e.touches[0].clientY); - } - }; - - // 绑定事件 - content.addEventListener('mousedown', onMouseDown); - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onEnd); - content.addEventListener('touchstart', onTouchStart, { passive: false }); - document.addEventListener('touchmove', onTouchMove, { passive: false }); - document.addEventListener('touchend', onEnd); - - return () => { - content.removeEventListener('mousedown', onMouseDown); - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onEnd); - content.removeEventListener('touchstart', onTouchStart); - document.removeEventListener('touchmove', onTouchMove); - document.removeEventListener('touchend', onEnd); - document.body.style.userSelect = ''; - }; - }; - - // 缩放功能 - const zoomIn = () => { - const svg = getSvg(); - if (svg) { - scale.value = Math.min(scale.value + 0.2, 10); - updateTransform(svg); - } - }; - - const zoomOut = () => { - const svg = getSvg(); - if (svg) { - scale.value = Math.max(scale.value - 0.2, 0.1); - updateTransform(svg); - } - }; - - const reset = () => { - const svg = getSvg(); - if (svg) { - resetState(); - updateTransform(svg); - } - }; - - const fullscreen = () => { - if (!container.value) - return; - - if (document.fullscreenElement) { - document.exitFullscreen(); - } - else { - container.value.requestFullscreen?.(); - } - }; - - const initialize = () => { - if (!container.value) - return; - - resetState(); - - const svg = getSvg(); - if (svg) { - removeEvents = addDragEvents(svg); - updateTransform(svg); - } - }; - - const destroy = () => { - removeEvents?.(); - removeEvents = null; - resetState(); - }; - - // 监听容器变化 - watch( - () => container.value, - () => { - destroy(); - resetState(); - } - ); - - // 组件卸载时清理 - onUnmounted(destroy); - - return { - zoomIn, - zoomOut, - reset, - fullscreen, - destroy, - initialize - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/usePlugins.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/usePlugins.ts deleted file mode 100644 index a8147689..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/usePlugins.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { Pluggable } from 'unified'; -import rehypeKatex from 'rehype-katex'; -import rehypeRaw from 'rehype-raw'; -import remarkBreaks from 'remark-breaks'; -import remarkGfm from 'remark-gfm'; -import remarkMath from 'remark-math'; -import { computed, toRefs } from 'vue'; -import { rehypeAnimatedPlugin } from '../plugins/rehypePlugin'; - -function usePlugins(props: any) { - const { - allowHtml, - enableAnimate, - enableLatex, - enableBreaks, - rehypePlugins, - remarkPlugins, - rehypePluginsAhead, - remarkPluginsAhead - } = toRefs(props); - - const rehype = computed(() => { - return [ - ...(rehypePluginsAhead.value as Pluggable[]), - allowHtml.value && rehypeRaw, - enableLatex.value && rehypeKatex, - enableAnimate.value && rehypeAnimatedPlugin, - ...(rehypePlugins.value as Pluggable[]) - ].filter(Boolean) as Pluggable[]; - }); - - const remark = computed(() => { - const base: (Pluggable | { plugins: Pluggable[] })[] = [ - enableLatex.value && remarkMath, - enableBreaks.value && remarkBreaks - ].filter(Boolean) as (Pluggable | { plugins: Pluggable[] })[]; - - return [ - [remarkGfm, { singleTilde: false }], - ...(remarkPluginsAhead.value as (Pluggable | { plugins: Pluggable[] })[]), - ...base, - ...(remarkPlugins.value as (Pluggable | { plugins: Pluggable[] })[]) - ]; - }); - - return { - rehypePlugins: rehype, - remarkPlugins: remark - }; -} -export { usePlugins }; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShiki.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShiki.ts deleted file mode 100644 index 84cf9a9f..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShiki.ts +++ /dev/null @@ -1,168 +0,0 @@ -import type { Root } from 'hast'; -import type { - BundledHighlighterOptions, - CodeToHastOptions, - CodeToTokensBaseOptions, - CodeToTokensOptions, - CodeToTokensWithThemesOptions, - GrammarState, - HighlighterGeneric, - RequireKeys, - ThemedToken, - ThemedTokenWithVariants, - TokensResult -} from 'shiki'; -import { GLOBAL_SHIKI_KEY } from '@components/XMarkdownCore/shared'; -import { - createdBundledHighlighter, - createOnigurumaEngine, - createSingletonShorthands -} from 'shiki'; -import { onUnmounted, provide, ref } from 'vue'; -import { languageLoaders, themeLoaders } from '../../../hooks/shiki-loader'; - -export interface GlobalShiki { - codeToHtml: ( - code: string, - options: CodeToHastOptions - ) => Promise ; - codeToHast: ( - code: string, - options: CodeToHastOptions - ) => Promise ; - codeToTokensBase: ( - code: string, - options: RequireKeys< - CodeToTokensBaseOptions , - 'theme' | 'lang' - > - ) => Promise ; - codeToTokens: ( - code: string, - options: CodeToTokensOptions - ) => Promise ; - codeToTokensWithThemes: ( - code: string, - options: RequireKeys< - CodeToTokensWithThemesOptions , - 'lang' | 'themes' - > - ) => Promise ; - getSingletonHighlighter: ( - options?: Partial > - ) => Promise >; - getLastGrammarState: - | ((element: ThemedToken[][] | Root) => GrammarState) - | (( - code: string, - options: CodeToTokensBaseOptions - ) => Promise ); -} - -/** - * @description Shiki 管理器(单例 + 懒初始化) - */ -class ShikiManager { - private static instance: ShikiManager | null = null; - - private shikiInstance: GlobalShiki | null = null; - - private constructor() {} - - static getInstance(): ShikiManager { - if (!ShikiManager.instance) { - ShikiManager.instance = new ShikiManager(); - } - return ShikiManager.instance; - } - - public getShiki(): GlobalShiki { - if (this.shikiInstance) return this.shikiInstance; - - const highlighterFactory = createdBundledHighlighter({ - langs: languageLoaders, - themes: themeLoaders, - engine: () => createOnigurumaEngine(import('shiki/wasm')) - }); - - const { - codeToHtml, - codeToHast, - codeToTokensBase, - codeToTokens, - codeToTokensWithThemes, - getSingletonHighlighter, - getLastGrammarState - } = createSingletonShorthands(highlighterFactory); - - this.shikiInstance = { - codeToHtml, - codeToHast, - codeToTokensBase, - codeToTokens, - codeToTokensWithThemes, - getSingletonHighlighter, - getLastGrammarState - }; - return this.shikiInstance; - } - - public dispose() { - this.shikiInstance = null; - ShikiManager.instance = null; - } -} - -// 全局状态管理 -let globalShikiInstance: GlobalShiki | undefined; -let globalShikiManager: ShikiManager | undefined; -let referenceCount = 0; - -const shikiIsCreated = ref(false); -const shikiInstance = ref (); -const shikiManager = ref (); - -/** - * @description 在 Vue 中提供 Shiki 实例(支持多组件实例) - */ -export function useShiki(): GlobalShiki { - // 增加引用计数 - referenceCount++; - - // ✅ 注册 onUnmounted 钩子 - onUnmounted(() => { - referenceCount--; - console.log(`shiki reference count: ${referenceCount}`); - - // 只有当所有组件都卸载时才清理 - if (referenceCount === 0) { - console.log('shiki destroyed - all references removed'); - shikiIsCreated.value = false; - shikiInstance.value = undefined; - shikiManager.value?.dispose(); - globalShikiManager?.dispose(); - globalShikiInstance = undefined; - globalShikiManager = undefined; - } - }); - - // ✅ 仅在首次时初始化 - if (!globalShikiInstance) { - console.log('shiki created'); - globalShikiManager = ShikiManager.getInstance(); - globalShikiInstance = globalShikiManager.getShiki(); - - shikiManager.value = globalShikiManager; - shikiInstance.value = globalShikiInstance; - - provide(GLOBAL_SHIKI_KEY, shikiInstance); - shikiIsCreated.value = true; - } else { - // 为后续组件实例提供相同的实例 - shikiManager.value = globalShikiManager; - shikiInstance.value = globalShikiInstance; - provide(GLOBAL_SHIKI_KEY, shikiInstance); - } - - return globalShikiInstance; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShikiColors.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShikiColors.ts deleted file mode 100644 index 01ab10ee..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useShikiColors.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { - BundledLanguage, - BundledTheme, - HighlighterGeneric, - ThemeRegistrationResolved -} from 'shiki'; -import type { InitShikiOptions } from '../shared'; -import { createHighlighter } from 'shiki'; -import { shikiThemeDefault } from '../shared'; -import { useDarkModeWatcher } from './useThemeMode'; - -interface UseShikiOptions { - themes?: InitShikiOptions['themes']; -} - -const highlighter = - shallowRef >(); -const shikiThemeColor = ref (); -const hasCreated = ref(false); -const oldThemes = ref (); - -export function useGlobalShikiHighlighter(options?: UseShikiOptions) { - const { isDark } = useDarkModeWatcher(); - - const themeArr = computed(() => { - if (options?.themes) { - return Object.keys(options.themes).map(key => options.themes![key]); - } - return [shikiThemeDefault.light, shikiThemeDefault.dark]; - }); - - const updateThemeColor = () => { - if (!highlighter.value || !hasCreated.value) - return; - - const themeName = isDark.value ? themeArr.value[1] : themeArr.value[0]; - - shikiThemeColor.value = highlighter.value.getTheme(themeName as any); - }; - - const init = async () => { - if ( - hasCreated.value && - JSON.stringify(oldThemes.value) === JSON.stringify(options?.themes) - ) { - updateThemeColor(); - return; - } - - const themes = [...themeArr.value]; - if (!themes.length) - return; - - const newHighlighter = await createHighlighter({ - themes: themes as any[], - langs: [] - }); - - highlighter.value?.dispose?.(); - highlighter.value = newHighlighter; - oldThemes.value = options?.themes; - hasCreated.value = true; - - updateThemeColor(); - }; - - watch(isDark, updateThemeColor, { immediate: true }); - - const destroy = () => { - hasCreated.value = false; - highlighter.value?.dispose?.(); - }; - - return { - highlighter, - shikiThemeColor, - isDark, - init, - destroy - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useThemeMode.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useThemeMode.ts deleted file mode 100644 index c87db813..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/hooks/useThemeMode.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { onMounted, onUnmounted, ref } from 'vue'; - -export function useDarkModeWatcher() { - const isDark = ref(document.body.classList.contains('dark')); - - let observer: MutationObserver; - - onMounted(() => { - observer = new MutationObserver(() => { - isDark.value = document.body.classList.contains('dark'); - }); - - observer.observe(document.body, { - attributes: true, - attributeFilter: ['class'] // 只监听 class 变化 - }); - }); - - onUnmounted(() => { - observer && observer.disconnect(); - }); - - return { - isDark - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/index.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/index.ts deleted file mode 100644 index d74ac1ea..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './MarkdownRender'; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/plugins/rehypePlugin.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/plugins/rehypePlugin.ts deleted file mode 100644 index 78a926e7..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/plugins/rehypePlugin.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates -// SPDX-License-Identifier: MIT -import type { Element, ElementContent, Root } from 'hast'; -import type { BuildVisitor } from 'unist-util-visit'; -import { visit } from 'unist-util-visit'; - -export function rehypeAnimatedPlugin() { - return (tree: Root) => { - visit(tree, 'element', ((node: Element) => { - if ( - [ - 'p', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'li', - 'strong', - 'th', - 'td' - ].includes(node.tagName) && - node.children - ) { - const newChildren: Array = []; - for (const child of node.children) { - if (child.type === 'text') { - // @ts-expect-error Segmenter is not available in all environments - const segmenter = new Intl.Segmenter('zh', { granularity: 'word' }); - const segments = segmenter.segment(child.value); - const words = [...segments] - .map(segment => segment.segment) - .filter(Boolean); - words.forEach((word: string) => { - newChildren.push({ - children: [{ type: 'text', value: word }], - properties: { - className: 'x-markdown-animated-word' - }, - tagName: 'span', - type: 'element' - }); - }); - } else { - newChildren.push(child); - } - } - node.children = newChildren; - } - }) as BuildVisitor ); - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/constants.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/constants.ts deleted file mode 100644 index 0d07ce29..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/constants.ts +++ /dev/null @@ -1,160 +0,0 @@ -import type { GlobalShiki } from '@components/XMarkdownCore/hooks/useShiki'; -import type { CodeXProps } from '@components/XMarkdownCore/shared/types'; -import type { BuiltinTheme } from 'shiki'; -import type { PluggableList } from 'unified'; -import type { MermaidToolbarConfig } from '../components/Mermaid/types'; -import type { ElxRunCodeOptions } from '../components/RunCode/type'; -import type { CustomAttrs, SanitizeOptions } from '../core'; -import type { InitShikiOptions } from './shikiHighlighter'; - -export const shikiThemeDefault: InitShikiOptions['themes'] = { - light: 'vitesse-light', - dark: 'vitesse-dark' -}; - -export const DEFAULT_PROPS = { - markdown: '', - allowHtml: false, - enableLatex: true, - enableAnimate: false, - enableBreaks: true, - codeXProps: () => ({}), - codeXRender: () => ({}), - codeXSlot: () => ({}), - codeHighlightTheme: null, - customAttrs: () => ({}), - remarkPlugins: () => [], - remarkPluginsAhead: () => [], - rehypePlugins: () => [], - rehypePluginsAhead: () => [], - rehypeOptions: () => ({}), - sanitize: false, - sanitizeOptions: () => ({}), - mermaidConfig: () => ({}), - langs: () => [], - defaultThemeMode: '' as 'light' | 'dark', - themes: () => ({ ...shikiThemeDefault }), - colorReplacements: () => ({}), - needViewCodeBtn: true, - secureViewCode: false, - viewCodeModalOptions: () => ({}) -}; - -export const MARKDOWN_CORE_PROPS = { - markdown: { - type: String, - default: '' - }, - allowHtml: { - type: Boolean, - default: false - }, - enableLatex: { - type: Boolean, - default: true - }, - enableAnimate: { - type: Boolean, - default: false - }, - enableBreaks: { - type: Boolean, - default: true - }, - codeXProps: { - type: Object as PropType , - default: () => ({ - enableCodePreview: false, // 启动代码预览功能 - enableCodeCopy: true, // 启动代码复制功能 - enableThemeToggle: false, // 启动主题切换 - enableCodeLineNumber: false - }) - }, - codeXRender: { - type: Object, - default: () => ({}) - }, - codeXSlot: { - type: Object, - default: () => ({}) - }, - codeHighlightTheme: { - type: Object as PropType , - default: () => null - }, - customAttrs: { - type: Object as PropType , - default: () => ({}) - }, - remarkPlugins: { - type: Array as PropType , - default: () => [] - }, - remarkPluginsAhead: { - type: Array as PropType , - default: () => [] - }, - rehypePlugins: { - type: Array as PropType , - default: () => [] - }, - rehypePluginsAhead: { - type: Array as PropType , - default: () => [] - }, - rehypeOptions: { - type: Object as PropType >, - default: () => ({}) - }, - sanitize: { - type: Boolean, - default: false - }, - sanitizeOptions: { - type: Object as PropType , - default: () => ({}) - }, - mermaidConfig: { - type: Object as PropType >, - default: () => ({}) - }, - langs: { - type: Array as PropType , - default: () => [] - }, - defaultThemeMode: { - type: String as PropType<'light' | 'dark'>, - default: 'light' - }, - themes: { - type: Object as PropType , - default: () => - ({ - ...shikiThemeDefault - }) satisfies InitShikiOptions['themes'] - }, - colorReplacements: { - type: Object as PropType , - default: () => ({}) - }, - needViewCodeBtn: { - type: Boolean, - default: true - }, - secureViewCode: { - type: Boolean, - default: false - }, - viewCodeModalOptions: { - type: Object as PropType , - default: () => ({}) - }, - isDark: { - type: Boolean, - default: false - }, - globalShiki: { - type: Object as PropType , - default: () => ({}) - } -}; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/index.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/index.ts deleted file mode 100644 index 7d7c6613..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './markdownProvider'; -// export * from './markdownRenderer'; -export * from './shikiHighlighter'; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownProvider.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownProvider.ts deleted file mode 100644 index 875c96c9..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownProvider.ts +++ /dev/null @@ -1,6 +0,0 @@ -const MARKDOWN_PROVIDER_KEY = Symbol('vue-element-plus-x-markdown-provider'); -const GLOBAL_SHIKI_KEY = Symbol('vue-element-plus-x-markdown-shiki-provider'); - -const MERMAID_CACHE_KEY_LENGTH = 10000; - -export { GLOBAL_SHIKI_KEY, MARKDOWN_PROVIDER_KEY, MERMAID_CACHE_KEY_LENGTH }; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownRenderer.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownRenderer.ts deleted file mode 100644 index fbe80205..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/markdownRenderer.ts +++ /dev/null @@ -1,97 +0,0 @@ -import type { BuiltinTheme } from 'shiki'; -import type { PluggableList } from 'unified'; -import type { MermaidToolbarConfig } from '../components/Mermaid/types'; -import type { CustomAttrs, SanitizeOptions } from '../core'; -import type { InitShikiOptions } from './shikiHighlighter'; -import { shikiThemeDefault } from './shikiHighlighter'; - -const MarkdownProps = { - markdown: { - type: String, - default: '' - }, - allowHtml: { - type: Boolean, - default: false - }, - enableCodeLineNumber: { - type: Boolean, - default: false - }, - enableLatex: { - type: Boolean, - default: true - }, - enableAnimate: { - type: Boolean, - default: false - }, - enableBreaks: { - type: Boolean, - default: true - }, - codeXRender: { - type: Object, - default: () => ({}) - }, - codeXSlot: { - type: Object, - default: () => ({}) - }, - codeHighlightTheme: { - type: Object as PropType , - default: () => null - }, - customAttrs: { - type: Object as PropType , - default: () => ({}) - }, - remarkPlugins: { - type: Array as PropType , - default: () => [] - }, - remarkPluginsAhead: { - type: Array as PropType , - default: () => [] - }, - rehypePlugins: { - type: Array as PropType , - default: () => [] - }, - rehypePluginsAhead: { - type: Array as PropType , - default: () => [] - }, - rehypeOptions: { - type: Object as PropType >, - default: () => ({}) - }, - sanitize: { - type: Boolean, - default: false - }, - sanitizeOptions: { - type: Object as PropType , - default: () => ({}) - }, - mermaidConfig: { - type: Object as PropType >, - default: () => ({}) - }, - langs: { - type: Array as PropType , - default: () => [] - }, - defaultThemeMode: { - type: String as PropType<'light' | 'dark'>, - default: 'light' - }, - themes: { - type: Object as PropType , - default: () => - ({ - ...shikiThemeDefault - }) satisfies InitShikiOptions['themes'] - } -}; -export { MarkdownProps }; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/shikiHighlighter.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/shikiHighlighter.ts deleted file mode 100644 index 18e767c6..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/shikiHighlighter.ts +++ /dev/null @@ -1,270 +0,0 @@ -import type { - BundledLanguage, - BundledTheme, - LanguageInput, - StringLiteralUnion, - ThemeRegistrationAny -} from 'shiki'; - -// 初始化Shiki 高亮器配置 -export interface InitShikiOptions { - // 语言列表 - langs: Array | undefined; - // 主题列表 - themes: Partial< - Record< - string | 'light' | 'dark', - ThemeRegistrationAny | StringLiteralUnion - > - >; - /** - * 自定义当前主题下的代码颜色配置 - * - * 一个颜色名称到新颜色值的映射表。 - * - * 注意: 颜色的键必须以 `#` 开头,并且应为小写格式 ,否则不生效。 - * - * 如果主题本身也定义了 `colorReplacements`,这个映射会与其合并。 - * - * 最好和当前主题对应着修改 - * - * @template - * ```typescript - * { - * "vitesse-light": { - * "#ab5959": "#ff66ff" - * }, - * "vitesse-dark": { - * "#cb7676": "#ff0066" - * } - * } - * ``` - */ - colorReplacements: Record >; -} - -export const shikiThemeDefault: InitShikiOptions['themes'] = { - light: 'vitesse-light', - dark: 'vitesse-dark' -}; - -export const SHIKI_SUPPORT_LANGS = [ - 'abap', - 'actionscript-3', - 'ada', - 'apache', - 'apex', - 'apl', - 'applescript', - 'ara', - 'asm', - 'astro', - 'awk', - 'ballerina', - 'bat', - 'beancount', - 'berry', - 'bibtex', - 'bicep', - 'blade', - 'c', - 'cadence', - 'clarity', - 'clojure', - 'cmake', - 'cobol', - 'codeql', - 'coffee', - 'cpp', - 'crystal', - 'csharp', - 'css', - 'cue', - 'cypher', - 'd', - 'dart', - 'dax', - 'diff', - 'docker', - 'dream-maker', - 'elixir', - 'elm', - 'erb', - 'erlang', - 'fish', - 'fsharp', - 'gdresource', - 'gdscript', - 'gdshader', - 'gherkin', - 'git-commit', - 'git-rebase', - 'glimmer-js', - 'glimmer-ts', - 'glsl', - 'gnuplot', - 'go', - 'graphql', - 'groovy', - 'hack', - 'haml', - 'handlebars', - 'haskell', - 'hcl', - 'hjson', - 'hlsl', - 'html', - 'http', - 'imba', - 'ini', - 'java', - 'javascript', - 'jinja-html', - 'jison', - 'json', - 'json5', - 'jsonc', - 'jsonl', - 'jsonnet', - 'jssm', - 'jsx', - 'julia', - 'kotlin', - 'kusto', - 'latex', - 'less', - 'liquid', - 'lisp', - 'logo', - 'lua', - 'make', - 'markdown', - 'marko', - 'matlab', - 'mdc', - 'mdx', - 'mermaid', - 'mojo', - 'narrat', - 'nextflow', - 'nginx', - 'nim', - 'nix', - 'objective-c', - 'objective-cpp', - 'ocaml', - 'pascal', - 'perl', - 'php', - 'plsql', - 'postcss', - 'powerquery', - 'powershell', - 'prisma', - 'prolog', - 'proto', - 'pug', - 'puppet', - 'purescript', - 'python', - 'r', - 'raku', - 'razor', - 'reg', - 'rel', - 'riscv', - 'rst', - 'ruby', - 'rust', - 'sas', - 'sass', - 'scala', - 'scheme', - 'scss', - 'shaderlab', - 'shellscript', - 'shellsession', - 'smalltalk', - 'solidity', - 'sparql', - 'splunk', - 'sql', - 'ssh-config', - 'stata', - 'stylus', - 'svelte', - 'swift', - 'system-verilog', - 'tasl', - 'tcl', - 'tex', - 'toml', - 'tsx', - 'turtle', - 'twig', - 'typescript', - 'v', - 'vb', - 'verilog', - 'vhdl', - 'viml', - 'vue', - 'vue-html', - 'vyper', - 'wasm', - 'wenyan', - 'wgsl', - 'wolfram', - 'xml', - 'xsl', - 'yaml', - 'zenscript', - 'zig', - 'bash', - 'batch', - 'be', - 'c#', - 'cdc', - 'clj', - 'cmd', - 'console', - 'cql', - 'cs', - 'dockerfile', - 'erl', - 'f#', - 'fs', - 'fsl', - 'gjs', - 'gts', - 'hbs', - 'hs', - 'jade', - 'js', - 'kql', - 'makefile', - 'md', - 'nar', - 'nf', - 'objc', - 'perl6', - 'properties', - 'ps', - 'ps1', - 'py', - 'ql', - 'rb', - 'rs', - 'sh', - 'shader', - 'shell', - 'spl', - 'styl', - 'ts', - 'vim', - 'vimscript', - 'vy', - 'yml', - 'zsh', - '文言' -] as const; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/types.d.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/types.d.ts deleted file mode 100644 index 41f12705..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/shared/types.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { TVueMarkdownProps } from '../'; -import type { CodeBlockHeaderExpose } from '../components/CodeBlock/shiki-header'; -import type { ElxRunCodeOptions } from '../components/RunCode/type'; -import type { InitShikiOptions } from './shikiHighlighter'; - -export type MarkdownProps = { - allowHtml?: boolean; - enableLatex?: boolean; - enableAnimate?: boolean; - enableBreaks?: boolean; - codeXProps?: CodeXProps; - codeXRender?: Record ; - codeXSlot?: CodeBlockHeaderExpose & Record ; - codeHighlightTheme?: BuiltinTheme | null; - remarkPluginsAhead?: PluggableList; - rehypePluginsAhead?: PluggableList; - defaultThemeMode?: 'light' | 'dark'; - needViewCodeBtn?: boolean; - secureViewCode?: boolean; - viewCodeModalOptions?: ElxRunCodeOptions; - mermaidConfig?: Partial ; -} & Partial > & - Pick< - TVueMarkdownProps, - | 'markdown' - | 'customAttrs' - | 'remarkPlugins' - | 'rehypePlugins' - | 'sanitize' - | 'sanitizeOptions' - | 'rehypeOptions' - >; - -export type MarkdownProviderProps = Omit & - Partial >; - -export interface CodeXProps { - enableCodePreview?: boolean; // 启动代码预览功能 - enableCodeCopy?: boolean; // 启动代码复制功能 - enableThemeToggle?: boolean; // 启动主题切换 - enableCodeLineNumber?: boolean; // 开启行号 -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/animate.scss b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/animate.scss deleted file mode 100644 index 41b07518..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/animate.scss +++ /dev/null @@ -1,12 +0,0 @@ -@keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.x-markdown-animated-word { - animation: fadeIn 1s ease-in-out; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/index.scss b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/index.scss deleted file mode 100644 index aae16311..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import url('./katex.scss'); -@import url('./animate.scss'); diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/katex.scss b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/katex.scss deleted file mode 100644 index 5ad954e8..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/katex.scss +++ /dev/null @@ -1,7 +0,0 @@ -.katex-error { - color: var(--el-text-color-secondary) !important; -} -.katex-html { - overflow: auto hidden; - padding: 3px; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/shiki.scss b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/shiki.scss deleted file mode 100644 index 03090c45..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/style/shiki.scss +++ /dev/null @@ -1,240 +0,0 @@ -// // 媒体查询:当用户系统主题为暗色时 基于浏览器的主题色 -// @media (prefers-color-scheme: light) { -// body { -// --shiki-custom-brr-mini: 3px; -// --shiki-custom-brr: 5px; -// --shiki-custom-blur: 10px; -// --shiki-code-header-bg: #fafafa; -// --shiki-code-header-span-color: #575757; -// } -// } - -// // 媒体查询:当用户系统主题为暗色时 -// @media (prefers-color-scheme: dark) { -// body { -// --shiki-custom-brr-mini: 3px; -// --shiki-custom-brr: 5px; -// --shiki-custom-blur: 10px; -// --shiki-code-header-bg: #272727; -// --shiki-code-header-span-color: #fafafa; -// } - -// .shiki, -// .shiki span { -// color: var(--shiki-dark) !important; -// background-color: var(--shiki-dark-bg) !important; -// /* 可选,用于定义字体样式 */ -// font-style: var(--shiki-dark-font-style) !important; -// font-weight: var(--shiki-dark-font-weight) !important; -// text-decoration: var(--shiki-dark-text-decoration) !important; -// } -// } - -body { - --shiki-custom-brr-mini: 3px; - --shiki-custom-brr: 5px; - --shiki-custom-blur: 10px; - --shiki-code-header-bg: #fafafa; - --shiki-code-header-span-color: #575757; - --shiki-code-header-btn-bg: #ebedf0; -} - -body.dark { - --shiki-custom-brr-mini: 3px; - --shiki-custom-brr: 5px; - --shiki-custom-blur: 10px; - --shiki-code-header-bg: #272727; - --shiki-code-header-span-color: #fafafa; - --shiki-code-header-btn-bg: #3b3b3b; -} - -body.dark .shiki, -body.dark .shiki span { - color: var(--shiki-dark) !important; - background-color: var(--shiki-dark-bg) !important; - /* 可选,用于定义字体样式 */ - font-style: var(--shiki-dark-font-style) !important; - font-weight: var(--shiki-dark-font-weight) !important; - text-decoration: var(--shiki-dark-text-decoration) !important; -} - -.shiki { - position: relative; -} - -.elx-xmarkdown-container { - // background-color: var(--el-fill-color); - color: black; - padding: 5px 10px; - border-radius: var(--shiki-custom-brr); - - /* 表格整体边框 */ - table { - border-collapse: collapse; - width: 100%; - border: 1px solid #ddd; - } - - /* 表头样式 */ - th { - background-color: white; - border: 1px solid #ddd; - padding: 8px; - text-align: left; - } - - /* 表格行样式 */ - td { - border: 1px solid #ddd; - padding: 8px; - } - - tbody { - tr:nth-child(even) { - background-color: white; - } - - tr:nth-child(odd) { - background-color: var(--el-fill-color-lighter); - } - } - - pre code { - text-shadow: none !important; - // 设置代码字体样式 - span { - font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - font-size: 1rem; - line-height: 1.5rem; - text-align: left; - // white-space: pre-wrap; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - tab-size: 4; - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; - } - } - - pre div.pre-md { - position: relative; - border-radius: var(--shiki-custom-brr-mini); - border: 1px solid var(--el-border-color); - min-width: 180px !important; - .markdown-elxLanguage-header-div { - box-sizing: content-box !important; - position: sticky; - top: 0; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - padding: 5px 5px 5px 8px; - border-radius: var(--shiki-custom-brr-mini); - -webkit-backdrop-filter: blur(var(--shiki-custom-blur)); - backdrop-filter: blur(var(--shiki-custom-blur)); - margin: 0; - background-color: var(--shiki-code-header-bg); - z-index: 1; - span { - box-shadow: none !important; - background-color: transparent !important; - } - - .markdown-elxLanguage-header-span { - font-size: 14px; - font-family: sans-serif; - color: var(--shiki-code-header-span-color) !important; - font-weight: bold !important; - background-color: transparent !important; - -webkit-user-select: none; - user-select: none; - &:hover { - cursor: pointer !important; - } - } - - .markdown-elxLanguage-header-space-start, - .markdown-elxLanguage-header-space { - display: flex; - justify-content: flex-end; - align-items: center; - } - - .markdown-elxLanguage-header-space-start { - justify-content: flex-start; - } - - .shiki-header-button { - border: 1px solid transparent; - border-radius: var(--shiki-custom-brr); - background-color: transparent; - width: fit-content !important; - padding: 0px 3px; - height: 24px; - opacity: 1; - transition: color 0.3s ease-in-out; - cursor: pointer; - .el-icon { - font-size: 15px !important; - } - svg { - width: 15px; - height: 15px; - } - .el-icon, - span { - background-color: transparent !important; - color: var(--shiki-code-header-span-color) !important; - } - &:hover { - background-color: var(--shiki-code-header-btn-bg); - } - } - // 按钮图标的大小 - .shiki-header-button { - span { - width: 16px; - height: 16px; - } - } - .shiki-header-button-expand { - span { - width: 12px; - height: 12px; - } - } - - .markdown-elxLanguage-header-toggle-expand { - margin: 2px 0 0 0; - transition: transform 0.3s ease-in-out; - transform: rotate(-90deg); - } - - .copied { - color: var(--el-color-success); - } - } - - code { - flex: 1; - box-sizing: border-box; - width: 100%; - height: 0; - } - } - - pre div.is-expanded { - height: auto !important; - code { - padding: 8px !important; - height: auto !important; - } - .markdown-elxLanguage-header-toggle-expand { - transform: rotate(0deg) !important; - } - } -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/dom-speech-recognition-env.d.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/dom-speech-recognition-env.d.ts deleted file mode 100644 index 6f76bcf7..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/dom-speech-recognition-env.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -/// - -declare interface SpeechRecognition { - continuous: boolean; - interimResults: boolean; - lang: string; - onresult: (event: SpeechRecognitionEvent) => void; - onstart: () => void; - onend: () => void; - onerror: (event: SpeechRecognitionError) => void; - start: () => void; - stop: () => void; -} - -declare interface SpeechRecognitionEvent { - results: SpeechRecognitionResultList; - resultIndex: number; -} - -declare interface SpeechRecognitionResultList { - [index: number]: SpeechRecognitionResult; - length: number; -} - -declare interface SpeechRecognitionResult { - [index: number]: SpeechRecognitionAlternative; - length: number; - isFinal: boolean; -} - -declare interface SpeechRecognitionAlternative { - confidence: number; - transcript: string; -} - -declare interface SpeechRecognitionError { - error: string; - message: string; -} - -declare const webkitSpeechRecognition: { - new (): SpeechRecognition; - prototype: SpeechRecognition; -}; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/XRequest.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/XRequest.ts deleted file mode 100644 index 157edd34..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/XRequest.ts +++ /dev/null @@ -1,187 +0,0 @@ -type OnError = (eventSource: EventSource, event: Event) => void; - -type BaseFetchOptions = Omit & { - headers?: HeadersInit | Headers; -}; - -type Transformer = (message: string) => T; - -interface BaseSSEProps { - baseURL?: string; - type?: SSEType; - onFinish?: (data: T[]) => void; - onAbort?: (data: T[]) => void; - transformer?: Transformer ; - onMessage?: (message: T) => void; -} - -interface SSEWithFetchProps { - baseOptions?: BaseFetchOptions; - onError?: (e: unknown) => void; -} - -interface SSEWithSSEProps { - baseOptions?: EventSourceInit; - onError?: OnError; - onOpen?: () => void; -} - -type SSEType = 'fetch' | 'sse' | 'sip'; - -/** - * @deprecated 已经废弃, 请使用 hook-fetch 代替 - * @deprecated This class has been deprecated, please use hook-fetch instead. - * - * @see {@link https://jsonlee12138.github.io/hook-fetch/ | hook-fetch 官方文档} - * @see {@link https://jsonlee12138.github.io/hook-fetch/ | hook-fetch Document} - */ -export type SSEProps = BaseSSEProps & - (SSEWithSSEProps | SSEWithFetchProps); - -/** - * @deprecated 已经废弃, 请使用 hook-fetch 代替 - * @deprecated This class has been deprecated, please use hook-fetch instead. - * - * @see {@link https://jsonlee12138.github.io/hook-fetch/ | hook-fetch 官方文档} - * @see {@link https://jsonlee12138.github.io/hook-fetch/ | hook-fetch Document} - */ -export class XRequest { - #instance: EventSource | null = null; - #transformer?: Transformer ; - #baseURL: string; - #baseOptions?: EventSourceInit | BaseFetchOptions; - #onAbort?: BaseSSEProps ['onAbort']; - #onMessage?: BaseSSEProps ['onMessage']; - #onError?: SSEWithSSEProps['onError'] | SSEWithFetchProps['onError']; - #onOpen?: () => void; - #type: SSEType = 'sse'; - #controller: AbortController | null = null; - #onFinish?: BaseSSEProps ['onFinish']; - #messages: T[] = []; - constructor({ - baseURL, - onAbort, - onMessage, - onError, - baseOptions, - transformer, - type, - onFinish, - ...props - }: SSEProps = {}) { - this.#baseURL = baseURL ?? ''; - this.#baseOptions = baseOptions ?? {}; - onAbort && (this.#onAbort = onAbort); - onMessage && (this.#onMessage = onMessage); - onError && (this.#onError = onError); - onFinish && (this.#onFinish = onFinish); - (props as SSEWithSSEProps).onOpen && - (this.#onOpen = (props as SSEWithSSEProps).onOpen); - transformer && (this.#transformer = transformer); - type && (this.#type = type); - this.abort = this.abort.bind(this); - this.send = this.send.bind(this); - } - - #sendWithFetch(url: string, options: BaseFetchOptions = {}) { - this.#controller = new AbortController(); - const signal = this.#controller.signal; - const fetchOptions = { - ...options, - signal - } as RequestInit; - return fetch(this.#baseURL + url, fetchOptions) - .then(res => res.body) - .then(async body => { - if (!body) { - return Promise.reject( - new Error('Response body is null in stream mode') - ); - } - const reader = body.getReader(); - const decoder = new TextDecoder('utf-8'); - let done = false; - while (!done) { - const { value, done: streamDone } = await reader.read(); - done = streamDone; - if (streamDone) { - this.#onFinish?.(this.#messages); - return; - } - if (value) { - const chunk = decoder.decode(value, { stream: true }); - const chunkUse = chunk.startsWith('data: ') - ? chunk.slice(6) - : chunk; - try { - const res = this.#transformer - ? (this.#transformer as Transformer )(chunkUse) - : (chunkUse as T); - this.#messages.push(res); - this.#onMessage?.(res); - } - catch (error) { - (this.#onError as SSEWithFetchProps['onError'])?.(error); - this.#controller?.abort(); - return Promise.reject(error); - } - } - } - }) - .catch(err => { - if (err.name === 'AbortError') { - this.#onAbort?.(this.#messages); - return; - } - (this.#onError as SSEWithFetchProps['onError'])?.(err); - this.#controller?.abort(); - }); - } - - #sendWithSSE(url: string, options: EventSourceInit = {}) { - const es = new EventSource(this.#baseURL + url, { - ...this.#baseOptions, - ...options - }); - es.onmessage = e => { - const res = this.#transformer - ? this.#transformer(e.data) - : (e as MessageEvent ); - this.#onMessage?.(res as T); - }; - es.onopen = () => { - this.#onOpen?.(); - }; - es.onerror = (ev: Event) => { - if (es.readyState === EventSource.CLOSED) { - this.#onFinish?.(this.#messages); - } - else { - this.#onError?.(es, ev); - } - this.abort(); - }; - this.#instance = es; - return es; - } - - public send(url: string, options: EventSourceInit | BaseFetchOptions = {}) { - switch (this.#type) { - case 'fetch': - this.#sendWithFetch(url, options as BaseFetchOptions); - break; - default: - this.#sendWithSSE(url, options as EventSourceInit); - } - return this; - } - - public abort() { - this.#instance?.close?.(); - this.#instance = null; - this.#controller?.abort(); - this.#controller = null; - this.#onAbort?.(this.#messages); - this.#messages = []; - } -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/index.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/index.ts deleted file mode 100644 index 93f0106c..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './usePrism'; -export * from './useRecord'; -export * from './useSend'; -export * from './useXStream'; -export * from './XRequest'; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/shiki-loader.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/shiki-loader.ts deleted file mode 100644 index 02460757..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/shiki-loader.ts +++ /dev/null @@ -1,298 +0,0 @@ -// 支持的语言映射表(你可以根据实际支持的语言增删) -export const languageLoaders: Record Promise > = { - abap: () => import('shiki/dist/langs/abap.mjs'), - 'actionscript-3': () => import('shiki/dist/langs/actionscript-3.mjs'), - ada: () => import('shiki/dist/langs/ada.mjs'), - apache: () => import('shiki/dist/langs/apache.mjs'), - apex: () => import('shiki/dist/langs/apex.mjs'), - apl: () => import('shiki/dist/langs/apl.mjs'), - applescript: () => import('shiki/dist/langs/applescript.mjs'), - ara: () => import('shiki/dist/langs/ara.mjs'), - asm: () => import('shiki/dist/langs/asm.mjs'), - astro: () => import('shiki/dist/langs/astro.mjs'), - awk: () => import('shiki/dist/langs/awk.mjs'), - ballerina: () => import('shiki/dist/langs/ballerina.mjs'), - bash: () => import('shiki/dist/langs/bash.mjs'), - bat: () => import('shiki/dist/langs/bat.mjs'), - batch: () => import('shiki/dist/langs/batch.mjs'), - be: () => import('shiki/dist/langs/be.mjs'), - beancount: () => import('shiki/dist/langs/beancount.mjs'), - berry: () => import('shiki/dist/langs/berry.mjs'), - bibtex: () => import('shiki/dist/langs/bibtex.mjs'), - bicep: () => import('shiki/dist/langs/bicep.mjs'), - blade: () => import('shiki/dist/langs/blade.mjs'), - c: () => import('shiki/dist/langs/c.mjs'), - 'c#': () => import('shiki/dist/langs/csharp.mjs'), - cadence: () => import('shiki/dist/langs/cadence.mjs'), - cdc: () => import('shiki/dist/langs/cdc.mjs'), - clarity: () => import('shiki/dist/langs/clarity.mjs'), - clj: () => import('shiki/dist/langs/clj.mjs'), - clojure: () => import('shiki/dist/langs/clojure.mjs'), - cmake: () => import('shiki/dist/langs/cmake.mjs'), - cmd: () => import('shiki/dist/langs/cmd.mjs'), - cobol: () => import('shiki/dist/langs/cobol.mjs'), - codeql: () => import('shiki/dist/langs/codeql.mjs'), - coffee: () => import('shiki/dist/langs/coffee.mjs'), - console: () => import('shiki/dist/langs/console.mjs'), - cpp: () => import('shiki/dist/langs/cpp.mjs'), - cql: () => import('shiki/dist/langs/cql.mjs'), - crystal: () => import('shiki/dist/langs/crystal.mjs'), - cs: () => import('shiki/dist/langs/cs.mjs'), - csharp: () => import('shiki/dist/langs/csharp.mjs'), - css: () => import('shiki/dist/langs/css.mjs'), - cue: () => import('shiki/dist/langs/cue.mjs'), - cypher: () => import('shiki/dist/langs/cypher.mjs'), - d: () => import('shiki/dist/langs/d.mjs'), - dart: () => import('shiki/dist/langs/dart.mjs'), - dax: () => import('shiki/dist/langs/dax.mjs'), - diff: () => import('shiki/dist/langs/diff.mjs'), - docker: () => import('shiki/dist/langs/docker.mjs'), - dockerfile: () => import('shiki/dist/langs/dockerfile.mjs'), - 'dream-maker': () => import('shiki/dist/langs/dream-maker.mjs'), - elixir: () => import('shiki/dist/langs/elixir.mjs'), - elm: () => import('shiki/dist/langs/elm.mjs'), - erb: () => import('shiki/dist/langs/erb.mjs'), - erl: () => import('shiki/dist/langs/erl.mjs'), - erlang: () => import('shiki/dist/langs/erlang.mjs'), - 'f#': () => import('shiki/dist/langs/fsharp.mjs'), - fish: () => import('shiki/dist/langs/fish.mjs'), - fs: () => import('shiki/dist/langs/fs.mjs'), - fsharp: () => import('shiki/dist/langs/fsharp.mjs'), - fsl: () => import('shiki/dist/langs/fsl.mjs'), - gdresource: () => import('shiki/dist/langs/gdresource.mjs'), - gdscript: () => import('shiki/dist/langs/gdscript.mjs'), - gdshader: () => import('shiki/dist/langs/gdshader.mjs'), - gherkin: () => import('shiki/dist/langs/gherkin.mjs'), - 'git-commit': () => import('shiki/dist/langs/git-commit.mjs'), - 'git-rebase': () => import('shiki/dist/langs/git-rebase.mjs'), - gjs: () => import('shiki/dist/langs/gjs.mjs'), - 'glimmer-js': () => import('shiki/dist/langs/glimmer-js.mjs'), - 'glimmer-ts': () => import('shiki/dist/langs/glimmer-ts.mjs'), - glsl: () => import('shiki/dist/langs/glsl.mjs'), - gnuplot: () => import('shiki/dist/langs/gnuplot.mjs'), - go: () => import('shiki/dist/langs/go.mjs'), - graphql: () => import('shiki/dist/langs/graphql.mjs'), - groovy: () => import('shiki/dist/langs/groovy.mjs'), - gts: () => import('shiki/dist/langs/gts.mjs'), - hack: () => import('shiki/dist/langs/hack.mjs'), - haml: () => import('shiki/dist/langs/haml.mjs'), - handlebars: () => import('shiki/dist/langs/handlebars.mjs'), - haskell: () => import('shiki/dist/langs/haskell.mjs'), - hbs: () => import('shiki/dist/langs/hbs.mjs'), - hcl: () => import('shiki/dist/langs/hcl.mjs'), - hjson: () => import('shiki/dist/langs/hjson.mjs'), - hlsl: () => import('shiki/dist/langs/hlsl.mjs'), - hs: () => import('shiki/dist/langs/hs.mjs'), - html: () => import('shiki/dist/langs/html.mjs'), - http: () => import('shiki/dist/langs/http.mjs'), - imba: () => import('shiki/dist/langs/imba.mjs'), - ini: () => import('shiki/dist/langs/ini.mjs'), - jade: () => import('shiki/dist/langs/jade.mjs'), - java: () => import('shiki/dist/langs/java.mjs'), - javascript: () => import('shiki/dist/langs/javascript.mjs'), - 'jinja-html': () => import('shiki/dist/langs/jinja-html.mjs'), - jison: () => import('shiki/dist/langs/jison.mjs'), - js: () => import('shiki/dist/langs/js.mjs'), - json: () => import('shiki/dist/langs/json.mjs'), - json5: () => import('shiki/dist/langs/json5.mjs'), - jsonc: () => import('shiki/dist/langs/jsonc.mjs'), - jsonl: () => import('shiki/dist/langs/jsonl.mjs'), - jsonnet: () => import('shiki/dist/langs/jsonnet.mjs'), - jssm: () => import('shiki/dist/langs/jssm.mjs'), - jsx: () => import('shiki/dist/langs/jsx.mjs'), - julia: () => import('shiki/dist/langs/julia.mjs'), - kotlin: () => import('shiki/dist/langs/kotlin.mjs'), - kql: () => import('shiki/dist/langs/kql.mjs'), - kusto: () => import('shiki/dist/langs/kusto.mjs'), - latex: () => import('shiki/dist/langs/latex.mjs'), - less: () => import('shiki/dist/langs/less.mjs'), - liquid: () => import('shiki/dist/langs/liquid.mjs'), - lisp: () => import('shiki/dist/langs/lisp.mjs'), - logo: () => import('shiki/dist/langs/logo.mjs'), - lua: () => import('shiki/dist/langs/lua.mjs'), - make: () => import('shiki/dist/langs/make.mjs'), - makefile: () => import('shiki/dist/langs/makefile.mjs'), - markdown: () => import('shiki/dist/langs/markdown.mjs'), - marko: () => import('shiki/dist/langs/marko.mjs'), - matlab: () => import('shiki/dist/langs/matlab.mjs'), - md: () => import('shiki/dist/langs/md.mjs'), - mdc: () => import('shiki/dist/langs/mdc.mjs'), - mdx: () => import('shiki/dist/langs/mdx.mjs'), - mermaid: () => import('shiki/dist/langs/mermaid.mjs'), - mojo: () => import('shiki/dist/langs/mojo.mjs'), - nar: () => import('shiki/dist/langs/nar.mjs'), - narrat: () => import('shiki/dist/langs/narrat.mjs'), - nextflow: () => import('shiki/dist/langs/nextflow.mjs'), - nf: () => import('shiki/dist/langs/nf.mjs'), - nginx: () => import('shiki/dist/langs/nginx.mjs'), - nim: () => import('shiki/dist/langs/nim.mjs'), - nix: () => import('shiki/dist/langs/nix.mjs'), - objc: () => import('shiki/dist/langs/objc.mjs'), - 'objective-c': () => import('shiki/dist/langs/objective-c.mjs'), - 'objective-cpp': () => import('shiki/dist/langs/objective-cpp.mjs'), - ocaml: () => import('shiki/dist/langs/ocaml.mjs'), - pascal: () => import('shiki/dist/langs/pascal.mjs'), - perl: () => import('shiki/dist/langs/perl.mjs'), - perl6: () => import('shiki/dist/langs/perl6.mjs'), - php: () => import('shiki/dist/langs/php.mjs'), - plsql: () => import('shiki/dist/langs/plsql.mjs'), - postcss: () => import('shiki/dist/langs/postcss.mjs'), - powerquery: () => import('shiki/dist/langs/powerquery.mjs'), - powershell: () => import('shiki/dist/langs/powershell.mjs'), - prisma: () => import('shiki/dist/langs/prisma.mjs'), - prolog: () => import('shiki/dist/langs/prolog.mjs'), - properties: () => import('shiki/dist/langs/properties.mjs'), - proto: () => import('shiki/dist/langs/proto.mjs'), - ps: () => import('shiki/dist/langs/ps.mjs'), - ps1: () => import('shiki/dist/langs/ps1.mjs'), - pug: () => import('shiki/dist/langs/pug.mjs'), - puppet: () => import('shiki/dist/langs/puppet.mjs'), - purescript: () => import('shiki/dist/langs/purescript.mjs'), - py: () => import('shiki/dist/langs/py.mjs'), - python: () => import('shiki/dist/langs/python.mjs'), - ql: () => import('shiki/dist/langs/ql.mjs'), - r: () => import('shiki/dist/langs/r.mjs'), - raku: () => import('shiki/dist/langs/raku.mjs'), - razor: () => import('shiki/dist/langs/razor.mjs'), - rb: () => import('shiki/dist/langs/rb.mjs'), - reg: () => import('shiki/dist/langs/reg.mjs'), - rel: () => import('shiki/dist/langs/rel.mjs'), - riscv: () => import('shiki/dist/langs/riscv.mjs'), - rs: () => import('shiki/dist/langs/rs.mjs'), - rst: () => import('shiki/dist/langs/rst.mjs'), - ruby: () => import('shiki/dist/langs/ruby.mjs'), - rust: () => import('shiki/dist/langs/rust.mjs'), - sas: () => import('shiki/dist/langs/sas.mjs'), - sass: () => import('shiki/dist/langs/sass.mjs'), - scala: () => import('shiki/dist/langs/scala.mjs'), - scheme: () => import('shiki/dist/langs/scheme.mjs'), - scss: () => import('shiki/dist/langs/scss.mjs'), - sh: () => import('shiki/dist/langs/sh.mjs'), - shader: () => import('shiki/dist/langs/shader.mjs'), - shaderlab: () => import('shiki/dist/langs/shaderlab.mjs'), - shell: () => import('shiki/dist/langs/shell.mjs'), - shellscript: () => import('shiki/dist/langs/shellscript.mjs'), - shellsession: () => import('shiki/dist/langs/shellsession.mjs'), - smalltalk: () => import('shiki/dist/langs/smalltalk.mjs'), - solidity: () => import('shiki/dist/langs/solidity.mjs'), - sparql: () => import('shiki/dist/langs/sparql.mjs'), - spl: () => import('shiki/dist/langs/spl.mjs'), - splunk: () => import('shiki/dist/langs/splunk.mjs'), - sql: () => import('shiki/dist/langs/sql.mjs'), - 'ssh-config': () => import('shiki/dist/langs/ssh-config.mjs'), - stata: () => import('shiki/dist/langs/stata.mjs'), - styl: () => import('shiki/dist/langs/styl.mjs'), - stylus: () => import('shiki/dist/langs/stylus.mjs'), - svelte: () => import('shiki/dist/langs/svelte.mjs'), - swift: () => import('shiki/dist/langs/swift.mjs'), - 'system-verilog': () => import('shiki/dist/langs/system-verilog.mjs'), - tasl: () => import('shiki/dist/langs/tasl.mjs'), - tcl: () => import('shiki/dist/langs/tcl.mjs'), - tex: () => import('shiki/dist/langs/tex.mjs'), - toml: () => import('shiki/dist/langs/toml.mjs'), - ts: () => import('shiki/dist/langs/ts.mjs'), - tsx: () => import('shiki/dist/langs/tsx.mjs'), - turtle: () => import('shiki/dist/langs/turtle.mjs'), - twig: () => import('shiki/dist/langs/twig.mjs'), - typescript: () => import('shiki/dist/langs/typescript.mjs'), - v: () => import('shiki/dist/langs/v.mjs'), - vb: () => import('shiki/dist/langs/vb.mjs'), - verilog: () => import('shiki/dist/langs/verilog.mjs'), - vhdl: () => import('shiki/dist/langs/vhdl.mjs'), - vim: () => import('shiki/dist/langs/vim.mjs'), - viml: () => import('shiki/dist/langs/viml.mjs'), - vimscript: () => import('shiki/dist/langs/vimscript.mjs'), - vue: () => import('shiki/dist/langs/vue.mjs'), - 'vue-html': () => import('shiki/dist/langs/vue-html.mjs'), - vy: () => import('shiki/dist/langs/vy.mjs'), - vyper: () => import('shiki/dist/langs/vyper.mjs'), - wasm: () => import('shiki/dist/langs/wasm.mjs'), - wenyan: () => import('shiki/dist/langs/wenyan.mjs'), - wgsl: () => import('shiki/dist/langs/wgsl.mjs'), - wolfram: () => import('shiki/dist/langs/wolfram.mjs'), - xml: () => import('shiki/dist/langs/xml.mjs'), - xsl: () => import('shiki/dist/langs/xsl.mjs'), - yaml: () => import('shiki/dist/langs/yaml.mjs'), - yml: () => import('shiki/dist/langs/yml.mjs'), - zenscript: () => import('shiki/dist/langs/zenscript.mjs'), - zig: () => import('shiki/dist/langs/zig.mjs'), - zsh: () => import('shiki/dist/langs/zsh.mjs'), - 文言: () => import('shiki/dist/langs/wenyan.mjs') -}; - -export const themeLoaders: Record Promise > = { - andromeeda: () => import('shiki/dist/themes/andromeeda.mjs'), - 'aurora-x': () => import('shiki/dist/themes/aurora-x.mjs'), - 'ayu-dark': () => import('shiki/dist/themes/ayu-dark.mjs'), - 'catppuccin-frappe': () => import('shiki/dist/themes/catppuccin-frappe.mjs'), - 'catppuccin-latte': () => import('shiki/dist/themes/catppuccin-latte.mjs'), - 'catppuccin-macchiato': () => - import('shiki/dist/themes/catppuccin-macchiato.mjs'), - 'catppuccin-mocha': () => import('shiki/dist/themes/catppuccin-mocha.mjs'), - 'dark-plus': () => import('shiki/dist/themes/dark-plus.mjs'), - dracula: () => import('shiki/dist/themes/dracula.mjs'), - 'dracula-soft': () => import('shiki/dist/themes/dracula-soft.mjs'), - 'everforest-dark': () => import('shiki/dist/themes/everforest-dark.mjs'), - 'everforest-light': () => import('shiki/dist/themes/everforest-light.mjs'), - 'github-dark': () => import('shiki/dist/themes/github-dark.mjs'), - 'github-dark-default': () => - import('shiki/dist/themes/github-dark-default.mjs'), - 'github-dark-dimmed': () => - import('shiki/dist/themes/github-dark-dimmed.mjs'), - 'github-dark-high-contrast': () => - import('shiki/dist/themes/github-dark-high-contrast.mjs'), - 'github-light': () => import('shiki/dist/themes/github-light.mjs'), - 'github-light-default': () => - import('shiki/dist/themes/github-light-default.mjs'), - 'github-light-high-contrast': () => - import('shiki/dist/themes/github-light-high-contrast.mjs'), - 'gruvbox-dark-hard': () => import('shiki/dist/themes/gruvbox-dark-hard.mjs'), - 'gruvbox-dark-medium': () => - import('shiki/dist/themes/gruvbox-dark-medium.mjs'), - 'gruvbox-dark-soft': () => import('shiki/dist/themes/gruvbox-dark-soft.mjs'), - 'gruvbox-light-hard': () => - import('shiki/dist/themes/gruvbox-light-hard.mjs'), - 'gruvbox-light-medium': () => - import('shiki/dist/themes/gruvbox-light-medium.mjs'), - 'gruvbox-light-soft': () => - import('shiki/dist/themes/gruvbox-light-soft.mjs'), - houston: () => import('shiki/dist/themes/houston.mjs'), - 'kanagawa-dragon': () => import('shiki/dist/themes/kanagawa-dragon.mjs'), - 'kanagawa-lotus': () => import('shiki/dist/themes/kanagawa-lotus.mjs'), - 'kanagawa-wave': () => import('shiki/dist/themes/kanagawa-wave.mjs'), - laserwave: () => import('shiki/dist/themes/laserwave.mjs'), - 'light-plus': () => import('shiki/dist/themes/light-plus.mjs'), - 'material-theme': () => import('shiki/dist/themes/material-theme.mjs'), - 'material-theme-darker': () => - import('shiki/dist/themes/material-theme-darker.mjs'), - 'material-theme-lighter': () => - import('shiki/dist/themes/material-theme-lighter.mjs'), - 'material-theme-ocean': () => - import('shiki/dist/themes/material-theme-ocean.mjs'), - 'material-theme-palenight': () => - import('shiki/dist/themes/material-theme-palenight.mjs'), - 'min-dark': () => import('shiki/dist/themes/min-dark.mjs'), - 'min-light': () => import('shiki/dist/themes/min-light.mjs'), - monokai: () => import('shiki/dist/themes/monokai.mjs'), - 'night-owl': () => import('shiki/dist/themes/night-owl.mjs'), - nord: () => import('shiki/dist/themes/nord.mjs'), - 'one-dark-pro': () => import('shiki/dist/themes/one-dark-pro.mjs'), - 'one-light': () => import('shiki/dist/themes/one-light.mjs'), - plastic: () => import('shiki/dist/themes/plastic.mjs'), - poimandres: () => import('shiki/dist/themes/poimandres.mjs'), - red: () => import('shiki/dist/themes/red.mjs'), - 'rose-pine': () => import('shiki/dist/themes/rose-pine.mjs'), - 'rose-pine-dawn': () => import('shiki/dist/themes/rose-pine-dawn.mjs'), - 'rose-pine-moon': () => import('shiki/dist/themes/rose-pine-moon.mjs'), - 'slack-dark': () => import('shiki/dist/themes/slack-dark.mjs'), - 'slack-ochin': () => import('shiki/dist/themes/slack-ochin.mjs'), - 'snazzy-light': () => import('shiki/dist/themes/snazzy-light.mjs'), - 'solarized-dark': () => import('shiki/dist/themes/solarized-dark.mjs'), - 'solarized-light': () => import('shiki/dist/themes/solarized-light.mjs'), - 'synthwave-84': () => import('shiki/dist/themes/synthwave-84.mjs'), - 'tokyo-night': () => import('shiki/dist/themes/tokyo-night.mjs'), - vesper: () => import('shiki/dist/themes/vesper.mjs'), - 'vitesse-black': () => import('shiki/dist/themes/vitesse-black.mjs'), - 'vitesse-dark': () => import('shiki/dist/themes/vitesse-dark.mjs'), - 'vitesse-light': () => import('shiki/dist/themes/vitesse-light.mjs') -}; diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/usePrism.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/usePrism.ts deleted file mode 100644 index ba3aa6f7..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/usePrism.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Prism from 'prismjs'; - -export function usePrism() { - const highlight = (code: string, lang: string) => { - try { - const grammar = Prism.languages[lang]; - if (grammar) { - return Prism.highlight(code, grammar, lang); - } - return code; - } - catch { - return code; - } - }; - return highlight; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useRecord.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useRecord.ts deleted file mode 100644 index a621ecf2..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useRecord.ts +++ /dev/null @@ -1,141 +0,0 @@ -export interface UseRecordError { - code: number; - message: string; -} - -export interface UseRecordProps { - onError?: (err: SpeechRecognitionErrorEvent | UseRecordError) => void; - onStart?: () => void; - onEnd?: (v: string) => void; - onResult?: (result: string) => void; -} - -/** - * @description Provides a hook for speech recognition, allowing voice input and handling various events such as start, end, result, and errors. - * @description 提供语音识别的钩子,允许语音输入并处理开始、结束、结果及错误等各种事件。 - * - * @interface UseRecordError - * Represents the structure of an error object for useRecord. - * 表示 useRecord 的错误对象结构。 - * - * @property {number} code - The error code. - * @property {number} code - 错误代码。 - * - * @property {string} message - The error message. - * @property {string} message - 错误信息。 - * - * @interface UseRecordProps - * Represents the configuration options for useRecord. - * 表示 useRecord 的配置选项。 - * - * @property {Function} [onError] - Callback function triggered when an error occurs. - * @property {Function} [onError] - 当发生错误时触发的回调函数。 - * - * @property {Function} [onStart] - Callback function triggered when voice recognition starts. - * @property {Function} [onStart] - 当语音识别开始时触发的回调函数。 - * - * @property {Function} [onEnd] - Callback function triggered when voice recognition ends, providing the final recognized text. - * @property {Function} [onEnd] - 当语音识别结束时触发的回调函数,并提供最终识别的文本。 - * - * @property {Function} [onResult] - Callback function triggered when intermediate recognition results are available. - * @property {Function} [onResult] - 当有中间识别结果时触发的回调函数。 - * - * @function useRecord - * Initializes the speech recognition functionality and returns state and control functions. - * 初始化语音识别功能并返回状态和控制函数。 - * - * @param {UseRecordProps} [props] - Configuration options for the hook. - * @param {UseRecordProps} [props] - 钩子的配置选项。 - * - * @returns {object} - An object containing the state and methods for speech recognition. - * @returns {object} - 包含语音识别状态和方法的对象。 - * - * @property {Ref } loading - Indicates whether speech recognition is currently active. - * @property {Ref } loading - 指示语音识别是否正在进行。 - * - * @property {Function} start - Starts the speech recognition process. - * @property {Function} start - 开始语音识别过程。 - * - * @property {Function} stop - Stops the speech recognition process. - * @property {Function} stop - 停止语音识别过程。 - * - * @property {Ref } value - Holds the recognized text. - * @property {Ref } value - 保存识别的文本。 - * - * @example - * const { loading, start, stop, value } = useRecord({ - * onStart: () => console.log('Recognition started'), - * onEnd: (result) => console.log('Recognition ended with result:', result), - * onResult: (result) => console.log('Intermediate result:', result), - * onError: (error) => console.error('Error:', error), - * }); - * - * start(); // Start voice recognition - * stop(); // Stop voice recognition - * console.log(value.value); // Access the recognized text - */ -export function useRecord({ - onError, - onStart, - onEnd, - onResult, -}: UseRecordProps = {}) { - const loading = ref (false); - const recognition = ref (null); - const value = ref (''); - - const start = () => { - if ('webkitSpeechRecognition' in window) { - recognition.value = new webkitSpeechRecognition(); - recognition.value!.continuous = true; - recognition.value.interimResults = true; - recognition.value.lang = 'zh-CN'; - recognition.value.onstart = () => { - loading.value = true; - value.value = ''; - onStart?.(); - }; - recognition.value.onend = () => { - loading.value = false; - onEnd?.(value.value); - }; - recognition.value.onerror = (e) => { - loading.value = false; - onError?.(e); - }; - recognition.value.onresult = (e) => { - let results = ''; - for (let i = 0; i <= e.resultIndex; i++) { - results += e.results[i][0].transcript; - } - value.value = results; - onResult?.(results); - }; - recognition.value.start(); - } - else { - onError?.({ - code: -1, - message: 'The current browser does not support voice recognition', - }); - } - }; - - const stop = () => { - if (recognition.value) { - recognition.value.stop(); - } - }; - - onUnmounted(() => { - stop(); - recognition.value = null; - }); - - return { - loading, - start, - stop, - value, - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useSend.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useSend.ts deleted file mode 100644 index cbc5e3e7..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useSend.ts +++ /dev/null @@ -1,74 +0,0 @@ -export interface UseSendProps { - onAbort?: () => void; - sendHandler?: (...args: any[]) => void; - abortHandler?: () => void; - finishHandler?: () => void; -} - -/** - * @description A utility function for handling the request status of send operation management, supporting optional abort functionality, as well as Promise and Server-Sent Events (SSE). - * @description 一个用于处理发送操作管理请求状态的实用函数,支持可选的中止功能,同时支持 Promise 和 SSE(服务端事件)。 - * - * @typedef {object} WithAbortProps - * @property {(signal: AbortSignal) => Promise } promise - A function that returns a promise and accepts an `AbortSignal` for cancellation. - * @property {(signal: AbortSignal) => Promise } promise - 一个返回 Promise 的函数,接受一个用于取消的 `AbortSignal`。 - * - * @typedef {object} WithSSEProps - * @property {EventSource} eventSource - An `EventSource` instance for handling Server-Sent Events. - * @property {EventSource} eventSource - 用于处理服务端事件的 `EventSource` 实例。 - * - * @typedef {object} UseSendProps - * @property {WithAbortProps | WithSSEProps} props - Either `WithAbortProps` or `WithSSEProps`, depending on the use case. - * @property {WithAbortProps | WithSSEProps} props - 根据使用场景,传入 `WithAbortProps` 或 `WithSSEProps`。 - * @property {() => void} [onAbort] - Optional callback triggered when the operation is aborted. - * @property {() => void} [onAbort] - 可选的回调函数,在操作被中止时触发。 - * @property {(...args: any[]) => void} [sendHandler] - Optional handler function invoked before sending starts. - * @property {(...args: any[]) => void} [sendHandler] - 可选的处理函数,在发送开始前调用。 - * - * @param {UseSendProps} props - Configuration options for the `useSend` function. - * @param {UseSendProps} props - `useSend` 函数的配置选项。 - * - * @returns {object} - Returns an object containing utility methods and state. - * @returns {object} - 返回一个包含实用方法和状态的对象。 - * - * @property {Ref } loading - A reactive reference indicating whether a send operation is in progress. - * @property {Ref } loading - 一个响应式引用,指示是否正在执行发送操作。 - * - * @property {Promise | undefined} promise - The promise returned by the `promise` function, if provided. - * @property {Promise | undefined} promise - 如果提供了 `promise` 函数,则返回的 Promise。 - * - * @property {() => void} abort - A function to abort the ongoing operation, either by aborting the promise or closing the EventSource. - * @property {() => void} abort - 一个用于中止当前操作的函数,可以中止 Promise 或关闭 EventSource。 - * - * @property {(...args: any[]) => void} send - A function to initiate the send operation, invoking the `sendHandler` if provided. - * @property {(...args: any[]) => void} send - 一个用于启动发送操作的函数,如果提供了 `sendHandler` 则会调用。 - */ -export function useSend( - { onAbort, sendHandler, abortHandler }: UseSendProps = {} as UseSendProps -) { - const loading = ref (false); - - const handleSend = (...args: any[]) => { - if (loading.value) - return; - sendHandler?.(...args); - loading.value = true; - }; - - const handleAbort = () => { - loading.value = false; - abortHandler?.(); - onAbort?.(); - }; - - const handleFinish = () => { - loading.value = false; - }; - - return { - loading, - abort: handleAbort, - send: handleSend, - finish: handleFinish - }; -} diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useXStream.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useXStream.ts deleted file mode 100644 index e7631365..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/hooks/useXStream.ts +++ /dev/null @@ -1,159 +0,0 @@ -const DEFAULT_STREAM_SEPARATOR = '\n\n'; -const DEFAULT_PART_SEPARATOR = '\n'; -const DEFAULT_KV_SEPARATOR = ':'; - -// 工具函数 -const isValidString = (str: string) => (str ?? '').trim() !== ''; - -// TransformStream 实现 -function splitStream() { - let buffer = ''; - - return new TransformStream ({ - transform(chunk, controller) { - buffer += chunk; - const parts = buffer.split(DEFAULT_STREAM_SEPARATOR); - parts.slice(0, -1).forEach((part) => { - if (isValidString(part)) - controller.enqueue(part); - }); - buffer = parts[parts.length - 1]; - }, - flush(controller) { - if (isValidString(buffer)) - controller.enqueue(buffer); - }, - }); -} - -function splitPart() { - return new TransformStream ({ - transform(partChunk, controller) { - const lines = partChunk.split(DEFAULT_PART_SEPARATOR); - const sseEvent = lines.reduce ((acc, line) => { - const sepIndex = line.indexOf(DEFAULT_KV_SEPARATOR); - if (sepIndex === -1) - return acc; - - const key = line.slice(0, sepIndex); - if (!isValidString(key)) - return acc; - - const value = line.slice(sepIndex + 1); - return { ...acc, [key]: value }; - }, {}); - - if (Object.keys(sseEvent).length > 0) - controller.enqueue(sseEvent); - }, - }); -} - -// 类型定义 -export type SSEFields = 'data' | 'event' | 'id' | 'retry'; -export type SSEOutput = Partial >; - -export interface XStreamOptions