@siact/sime-x-vue 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1777,8 +1777,27 @@ function useTTS(getVoiceConfig) {
1777
1777
  let initPromise = null;
1778
1778
  let audioCtx = null;
1779
1779
  let sentenceBuffer = "";
1780
- const sentenceDelimiters = /[。!?;\n.!?;]/;
1781
- const stripMarkdown = (text) => text.replace(/```[\s\S]*?```/g, "").replace(/\|[^\n]*\|/g, "").replace(/#{1,6}\s*/g, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[-*+]\s+/g, "").replace(/>\s+/g, "").replace(/\n{2,}/g, "。").replace(/\n/g, ",").trim();
1780
+ const SENTENCE_DELIMITERS = /[。!?;\n!?;]/;
1781
+ const LEADING_WEAK_PUNCTUATION = /^[\s,、;:,.!?。]+/;
1782
+ const TRAILING_SENTENCE_PUNCTUATION = /[。!?.!?;;]$/;
1783
+ const findSentenceBoundary = (text) => {
1784
+ for (let i = 0; i < text.length; i++) {
1785
+ const char = text[i];
1786
+ if (SENTENCE_DELIMITERS.test(char)) {
1787
+ return i;
1788
+ }
1789
+ }
1790
+ return -1;
1791
+ };
1792
+ const stripMarkdown = (text) => text.replace(/```[\s\S]*?```/g, "").replace(/\|[^\n]*\|/g, "").replace(/#{1,6}\s*/g, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/^[-*+]\s+/gm, "").replace(/^>\s+/gm, "").replace(/\s*\+\s*/g, "加").replace(/\s*-\s*/g, "减").replace(/\s*×\s*/g, "乘").replace(/\s*÷\s*/g, "除").replace(/\s*=\s*/g, "等于").replace(/(\d)\.(\d)/g, "$1点$2").replace(/\n{2,}/g, "。").replace(/\n/g, ",").trim();
1793
+ const normalizeSpeakText = (text, options) => {
1794
+ const clean = stripMarkdown(text).replace(LEADING_WEAK_PUNCTUATION, "").trim();
1795
+ if (!clean) return "";
1796
+ if (options?.isFinalChunk && !TRAILING_SENTENCE_PUNCTUATION.test(clean)) {
1797
+ return `${clean}。`;
1798
+ }
1799
+ return clean;
1800
+ };
1782
1801
  const warmUpAudio = () => {
1783
1802
  if (!audioCtx || audioCtx.state === "closed") {
1784
1803
  try {
@@ -1847,7 +1866,7 @@ function useTTS(getVoiceConfig) {
1847
1866
  return initPromise;
1848
1867
  };
1849
1868
  const speak = async (text) => {
1850
- const clean = stripMarkdown(text);
1869
+ const clean = normalizeSpeakText(text);
1851
1870
  if (!clean.trim()) return;
1852
1871
  hasPendingAudio.value = true;
1853
1872
  const tts = await ensureInstance();
@@ -1861,17 +1880,20 @@ function useTTS(getVoiceConfig) {
1861
1880
  const feed = (delta) => {
1862
1881
  sentenceBuffer += delta;
1863
1882
  while (true) {
1864
- const match = sentenceBuffer.match(sentenceDelimiters);
1865
- if (!match || match.index === void 0) break;
1866
- const sentence = sentenceBuffer.slice(0, match.index + 1).trim();
1867
- sentenceBuffer = sentenceBuffer.slice(match.index + 1);
1883
+ const boundaryIndex = findSentenceBoundary(sentenceBuffer);
1884
+ if (boundaryIndex === -1) break;
1885
+ const sentence = sentenceBuffer.slice(0, boundaryIndex + 1).trim();
1886
+ sentenceBuffer = sentenceBuffer.slice(boundaryIndex + 1);
1868
1887
  if (sentence.length > 0) speak(sentence);
1869
1888
  }
1870
1889
  };
1871
1890
  const flush = () => {
1872
1891
  const remaining = sentenceBuffer.trim();
1873
1892
  sentenceBuffer = "";
1874
- if (remaining.length > 0) speak(remaining);
1893
+ if (remaining.length > 0) {
1894
+ const clean = normalizeSpeakText(remaining, { isFinalChunk: true });
1895
+ if (clean) void speak(clean);
1896
+ }
1875
1897
  };
1876
1898
  const stop = () => {
1877
1899
  sentenceBuffer = "";