refactor(stage-ui): replace grapheme cluster reader from clustr (#308)
This commit is contained in:
@@ -3,9 +3,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { Animator } from './animators'
|
||||
|
||||
import { readGraphemeClusters } from 'clustr'
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
/**
|
||||
* A plain string or a ReadableStream of bytes from text in UTF-8 encoding.
|
||||
* If a stream is provided, the stream **SHOULD NOT** be reused. (i.e. You should not set a same stream twice.)
|
||||
*/
|
||||
text?: string | ReadableStream<Uint8Array>
|
||||
textClass?: string | string[]
|
||||
animator?: Animator
|
||||
@@ -19,65 +24,6 @@ const targets = ref<string[]>([])
|
||||
const abortController = shallowRef<AbortController>()
|
||||
const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' })
|
||||
|
||||
function readGraphemeClusters(stream: ReadableStream<Uint8Array>, options?: { signal?: AbortSignal }) {
|
||||
const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
||||
const decoder = new TextDecoder('utf-8', { fatal: false })
|
||||
const reader = stream.getReader()
|
||||
const signal = options?.signal
|
||||
|
||||
async function* iterator() {
|
||||
let buf = ''
|
||||
|
||||
while (true) {
|
||||
if (signal?.aborted) {
|
||||
reader.cancel()
|
||||
throw new Error('Aborted')
|
||||
}
|
||||
|
||||
const readPromise = reader.read()
|
||||
let result
|
||||
if (signal) {
|
||||
result = await Promise.race([
|
||||
readPromise,
|
||||
new Promise((_, reject) => {
|
||||
signal.addEventListener('abort', () => reject(new Error('Aborted')), { once: true })
|
||||
}),
|
||||
])
|
||||
}
|
||||
else {
|
||||
result = await readPromise
|
||||
}
|
||||
|
||||
const { done, value } = result as ReadableStreamReadResult<Uint8Array<ArrayBufferLike>>
|
||||
if (done) {
|
||||
const segments = [...segmenter.segment(buf)]
|
||||
for (const seg of segments) {
|
||||
yield seg.segment
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
buf += decoder.decode(value, { stream: true })
|
||||
|
||||
const segments = [...segmenter.segment(buf)]
|
||||
if (segments.length > 1) {
|
||||
const last = segments.pop()!
|
||||
for (const seg of segments) {
|
||||
yield seg.segment
|
||||
}
|
||||
buf = buf.slice(last.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const asyncIterator = iterator()
|
||||
|
||||
return {
|
||||
iterator: asyncIterator,
|
||||
[Symbol.asyncIterator]() { return asyncIterator },
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.text, async (text) => {
|
||||
if (!text)
|
||||
return
|
||||
@@ -89,7 +35,7 @@ watch(() => props.text, async (text) => {
|
||||
abortController.value = new AbortController()
|
||||
try {
|
||||
targets.value = []
|
||||
for await (const cluster of readGraphemeClusters(text, { signal: abortController.value.signal })) {
|
||||
for await (const cluster of readGraphemeClusters(text.getReader(), { signal: abortController.value.signal })) {
|
||||
targets.value.push(cluster)
|
||||
emits('textSplit', cluster)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user