diff --git a/packages/components/package.json b/packages/components/package.json index fca4b65d6..31a846c9d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -64,7 +64,8 @@ "@tiptap/extension-text": "^3.11.0", "@tiptap/extension-history": "^3.11.0", "@tiptap/extension-placeholder": "^3.11.0", - "@tiptap/extension-character-count": "^3.11.0" + "@tiptap/extension-character-count": "^3.11.0", + "unicode-segmenter": "^0.14.5" }, "devDependencies": { "@types/dom-speech-recognition": "^0.0.6", diff --git a/packages/components/src/sender/composables/useEditor.ts b/packages/components/src/sender/composables/useEditor.ts index fa8d1bbc7..50b3922ca 100644 --- a/packages/components/src/sender/composables/useEditor.ts +++ b/packages/components/src/sender/composables/useEditor.ts @@ -13,6 +13,7 @@ import CharacterCount from '@tiptap/extension-character-count' import type { AnyExtension } from '@tiptap/core' import type { SenderEmits, UseEditorReturn } from '../index.type' import type { SenderPropsWithDefaults } from '../index.vue' +import { countGraphemes } from '../utils/countGraphemes' /** * 编辑器 Hook @@ -46,6 +47,7 @@ export function useEditor(props: SenderPropsWithDefaults, emit: SenderEmits): Us }), CharacterCount.configure({ mode: 'textSize', + textCounter: countGraphemes, }), ] diff --git a/packages/components/src/sender/composables/useSenderCore.ts b/packages/components/src/sender/composables/useSenderCore.ts index 4893f7d95..ee276f84b 100644 --- a/packages/components/src/sender/composables/useSenderCore.ts +++ b/packages/components/src/sender/composables/useSenderCore.ts @@ -27,6 +27,7 @@ import { useEditor } from './useEditor' import { useKeyboardShortcuts } from './useKeyboardShortcuts' import { useModeSwitch } from './useModeSwitch' import { useAutoSize } from './useAutoSize' +import { countGraphemes } from '../utils/countGraphemes' /** * useSenderCore 返回类型 @@ -76,8 +77,7 @@ export function useSenderCore(props: SenderPropsWithDefaults, emit: SenderEmits) const characterCount = computed(() => { if (!editor.value) return 0 - const text = getTextWithTemplates(editor.value) - return text.length + return countGraphemes(getTextWithTemplates(editor.value)) }) const isOverLimit = computed(() => { diff --git a/packages/components/src/sender/utils/countGraphemes.ts b/packages/components/src/sender/utils/countGraphemes.ts new file mode 100644 index 000000000..fa2bb7587 --- /dev/null +++ b/packages/components/src/sender/utils/countGraphemes.ts @@ -0,0 +1,22 @@ +import { countGraphemes as countGraphemesFallback } from 'unicode-segmenter/grapheme' + +const graphemeSegmenter = + typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function' + ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) + : null + +export function countGraphemes(text: string): number { + if (!text) return 0 + + if (graphemeSegmenter) { + let count = 0 + + for (const _segment of graphemeSegmenter.segment(text)) { + count++ + } + + return count + } + + return countGraphemesFallback(text) +} diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index d480f4997..80666f7f1 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -4,6 +4,7 @@ "useDefineForClassFields": true, "module": "ESNext", "lib": [ + "ES2022.Intl", "ES2020", "DOM", "DOM.Iterable"