sur-voice 1.0.0 → 1.0.1
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.
- package/.history/package_20260121114101.json +30 -0
- package/.history/package_20260121141349.json +30 -0
- package/.history/src/components/surVoice_20260121134911.vue +393 -0
- package/.history/src/components/surVoice_20260121140739.vue +393 -0
- package/.history/src/components/surVoice_20260121140740.vue +393 -0
- package/.history/src/components/surVoice_20260121140750.vue +393 -0
- package/.history/src/components/surVoice_20260121141112.vue +394 -0
- package/.history/src/components/surVoice_20260121141323.vue +412 -0
- package/.history/src/components/surVoice_20260121141324.vue +412 -0
- package/.history/src/components/surVoice_20260121141338.vue +414 -0
- package/.history/src/components/surVoice_20260121141339.vue +414 -0
- package/.history/src/components/surVoice_20260121141340.vue +414 -0
- package/.history/sur-voice/package_20260121113239.json +27 -0
- package/.history/sur-voice/package_20260121113252.json +27 -0
- package/.history/sur-voice/package_20260121113253.json +27 -0
- package/.history/sur-voice/package_20260121113812.json +30 -0
- package/.history/sur-voice/package_20260121113813.json +30 -0
- package/.history/sur-voice/package_20260121113851.json +30 -0
- package/.history/sur-voice/package_20260121114014.json +31 -0
- package/.history/sur-voice/package_20260121114015.json +31 -0
- package/.history/sur-voice/package_20260121114016.json +31 -0
- package/.history/sur-voice/package_20260121114017.json +31 -0
- package/.history/sur-voice/package_20260121114018.json +31 -0
- package/.history/sur-voice/package_20260121114102.json +30 -0
- package/.history/sur-voice/src/index_20260106155836.ts +18 -0
- package/.history/sur-voice/src/index_20260121113733.ts +18 -0
- package/.history/sur-voice/src/index_20260121113739.ts +18 -0
- package/.history/sur-voice/src/index_20260121113740.ts +18 -0
- package/.history/sur-voice/vite.config_20251126104257.ts +7 -0
- package/.history/sur-voice/vite.config_20260121113441.ts +23 -0
- package/.history/sur-voice/vite.config_20260121113652.ts +41 -0
- package/.history/sur-voice/vite.config_20260121113653.ts +41 -0
- package/.history/sur-voice/vite.config_20260121113700.ts +41 -0
- package/.history/sur-voice/vite.config_20260121113701.ts +41 -0
- package/.history/sur-voice/vite.config_20260121113705.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113706.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113920.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113926.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113927.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113928.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113929.ts +43 -0
- package/.history/sur-voice/vite.config_20260121113930.ts +43 -0
- package/package.json +1 -1
- package/src/components/surVoice.vue +26 -3
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="surVoice" v-if="!isSpeaking">
|
|
3
|
+
<input
|
|
4
|
+
style="width: 800px; height: 40px; padding: 0 10px"
|
|
5
|
+
v-model="inputText"
|
|
6
|
+
placeholder="请输入内容…"
|
|
7
|
+
/>
|
|
8
|
+
|
|
9
|
+
<!-- 录音开始按钮(父组件可用插槽自定义) -->
|
|
10
|
+
<slot
|
|
11
|
+
v-if="!setRecording"
|
|
12
|
+
name="start-record"
|
|
13
|
+
:start="startRecordAudio"
|
|
14
|
+
:recording="setRecording"
|
|
15
|
+
:textLoading="textLoading"
|
|
16
|
+
:inputText="inputText"
|
|
17
|
+
>
|
|
18
|
+
<img
|
|
19
|
+
style="width: 40px; height: 40px"
|
|
20
|
+
src="https://qiniu2.xuandou.vip/voice2.png"
|
|
21
|
+
class="voice"
|
|
22
|
+
@click="startRecordAudio()"
|
|
23
|
+
/>
|
|
24
|
+
</slot>
|
|
25
|
+
|
|
26
|
+
<!-- 录音结束按钮(父组件可用插槽自定义) -->
|
|
27
|
+
<slot
|
|
28
|
+
v-if="setRecording"
|
|
29
|
+
name="stop-record"
|
|
30
|
+
:stop="stopRecordAudio"
|
|
31
|
+
:recording="setRecording"
|
|
32
|
+
:textLoading="textLoading"
|
|
33
|
+
:inputText="inputText"
|
|
34
|
+
>
|
|
35
|
+
<img
|
|
36
|
+
style="width: 40px; height: 40px"
|
|
37
|
+
src="https://qiniu2.xuandou.vip/searchforSound.png"
|
|
38
|
+
class="voice"
|
|
39
|
+
@click="stopRecordAudio()"
|
|
40
|
+
/>
|
|
41
|
+
</slot>
|
|
42
|
+
|
|
43
|
+
<div>{{ !textLoading ? "点击开始录音" : "" }}</div>
|
|
44
|
+
<div>{{ textLoading == "start" ? "正在录音" : "" }}</div>
|
|
45
|
+
<div>{{ textLoading == "loading" ? "文字转化中..." : "" }}</div>
|
|
46
|
+
<div>{{ textLoading == "finish" ? "文字转换完成" : "" }}</div>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
import { ref, watch } from "vue";
|
|
52
|
+
import { sha256 } from "js-sha256";
|
|
53
|
+
import Recorder from "@/utils/Recorder";
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(
|
|
56
|
+
defineProps<{
|
|
57
|
+
appKey?: string;
|
|
58
|
+
secret?: string;
|
|
59
|
+
}>(),
|
|
60
|
+
{
|
|
61
|
+
appKey: "", // 3k5jkbo5jsngmjmij7fy77xyrcoud5xkinbaqnik
|
|
62
|
+
secret: "", // 01a1fe93e03bbfb775ad1a6482a130c2
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const emit = defineEmits<{
|
|
67
|
+
(e: "update:inputText", v: string): void;
|
|
68
|
+
}>();
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
//!!!!!!!!!!!!!!!!!!!!! 录音(ASR)
|
|
72
|
+
|
|
73
|
+
// 目标:减少从说话到首包文字返回的延迟
|
|
74
|
+
// - 去掉每帧 console.log(会严重阻塞主线程,导致 onmessage 延迟)
|
|
75
|
+
// - 聚合/节流发送(降低 ws.send 频率,让服务端更快开始解码)
|
|
76
|
+
// - 背压控制(bufferedAmount 太大时不再堆积,避免“越发越慢”)
|
|
77
|
+
const inputText = ref("");
|
|
78
|
+
const isSpeaking = ref(false); // 是否正在说话
|
|
79
|
+
const textLoading: any = ref(null);
|
|
80
|
+
const DEBUG_ASR = false;
|
|
81
|
+
|
|
82
|
+
// 将 inputText 实时回传给父组件(作为回调)
|
|
83
|
+
watch(
|
|
84
|
+
inputText,
|
|
85
|
+
(v) => {
|
|
86
|
+
if(v){
|
|
87
|
+
emit("update:inputText", v);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{ immediate: true }
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
let ctime = ref(0);
|
|
95
|
+
let intervalHandler: any;
|
|
96
|
+
let recorder: any; // 录音实例
|
|
97
|
+
const setRecording = ref(false); // 是否正在录音
|
|
98
|
+
const messageContent = ref("");
|
|
99
|
+
const result = ref<any[]>([]); // 语音识别结果
|
|
100
|
+
let voiceWs: WebSocket | null = null;
|
|
101
|
+
|
|
102
|
+
// 发送统计
|
|
103
|
+
let count = 0;
|
|
104
|
+
let lastStatsAt = 0;
|
|
105
|
+
|
|
106
|
+
// 音频分块聚合队列(ArrayBufferView -> Blob)
|
|
107
|
+
const voiceSendQueue: ArrayBufferView[] = [];
|
|
108
|
+
let voiceSendQueueBytes = 0; // 已入队音频字节数
|
|
109
|
+
let voiceFlushTimer: number | null = null; // VoiceWS 发送循环定时器
|
|
110
|
+
|
|
111
|
+
// 经验值:40ms flush 一次;每次包大约 40-120ms 音频,服务端更容易快速出首包
|
|
112
|
+
const VOICE_FLUSH_INTERVAL_MS = 40;
|
|
113
|
+
// 队列上限:超过就丢弃旧数据,避免出现 10s+ 的排队延迟
|
|
114
|
+
const VOICE_MAX_QUEUE_BYTES = 1024 * 1024;
|
|
115
|
+
// ws 背压:浏览器内部缓冲超过该值,暂停 flush
|
|
116
|
+
const VOICE_MAX_BUFFERED_AMOUNT = 512 * 1024;
|
|
117
|
+
|
|
118
|
+
let msgArr: any[] = []; // 实时识别片段
|
|
119
|
+
|
|
120
|
+
// STEP 1: 开始录音
|
|
121
|
+
const startRecordAudio = () => {
|
|
122
|
+
// 防止重复点击导致多路并发
|
|
123
|
+
textLoading.value = "start";
|
|
124
|
+
if (setRecording.value) return;
|
|
125
|
+
|
|
126
|
+
resetAsrState();
|
|
127
|
+
|
|
128
|
+
setRecording.value = true;
|
|
129
|
+
intervalHandler = setInterval(() => {
|
|
130
|
+
ctime.value = ctime.value + 1;
|
|
131
|
+
if (ctime.value > 60) {
|
|
132
|
+
endRecording();
|
|
133
|
+
}
|
|
134
|
+
}, 1000);
|
|
135
|
+
|
|
136
|
+
doRecording();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// STEP 2: 重置 ASR 状态
|
|
140
|
+
function resetAsrState() {
|
|
141
|
+
count = 0;
|
|
142
|
+
lastStatsAt = 0;
|
|
143
|
+
msgArr = [];
|
|
144
|
+
result.value = [];
|
|
145
|
+
messageContent.value = "";
|
|
146
|
+
voiceSendQueue.length = 0;
|
|
147
|
+
voiceSendQueueBytes = 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// STEP 3: 录音处理
|
|
151
|
+
function doRecording() {
|
|
152
|
+
createVoiceWS();
|
|
153
|
+
|
|
154
|
+
recorder = new Recorder(onaudioprocess);
|
|
155
|
+
|
|
156
|
+
recorder.ready().then(
|
|
157
|
+
() => {
|
|
158
|
+
recorder.start();
|
|
159
|
+
},
|
|
160
|
+
() => {
|
|
161
|
+
voiceWs?.close();
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// STEP 3.1: 音频采集回调:这里要尽量轻量,否则会拖慢主线程,影响 onmessage
|
|
167
|
+
function onaudioprocess(chunk: any) {
|
|
168
|
+
if (DEBUG_ASR) console.log("onaudioprocess chunk");
|
|
169
|
+
if (!setRecording.value) return;
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if (voiceWs && voiceWs.readyState === WebSocket.OPEN) {
|
|
173
|
+
count++;
|
|
174
|
+
enqueueVoiceChunk(chunk);
|
|
175
|
+
|
|
176
|
+
// 降低发送频率:靠定时器 flush
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
if (DEBUG_ASR && now - lastStatsAt > 1000) {
|
|
179
|
+
lastStatsAt = now;
|
|
180
|
+
console.log("ASR stats", {
|
|
181
|
+
count,
|
|
182
|
+
bufferedAmount: voiceWs.bufferedAmount,
|
|
183
|
+
queueBytes: voiceSendQueueBytes,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// STEP 3.2: 音频分块聚合队列
|
|
190
|
+
function enqueueVoiceChunk(chunk: any) {
|
|
191
|
+
if (!chunk) return;
|
|
192
|
+
|
|
193
|
+
// Recorder.encodePCMFragment 返回 DataView(ArrayBufferView),这里统一转成 ArrayBufferView
|
|
194
|
+
const view: ArrayBufferView | null =
|
|
195
|
+
chunk instanceof DataView
|
|
196
|
+
? chunk
|
|
197
|
+
: ArrayBuffer.isView(chunk)
|
|
198
|
+
? (chunk as ArrayBufferView)
|
|
199
|
+
: chunk instanceof ArrayBuffer
|
|
200
|
+
? new Uint8Array(chunk)
|
|
201
|
+
: null;
|
|
202
|
+
|
|
203
|
+
if (!view) return;
|
|
204
|
+
|
|
205
|
+
// 如果 ws 还没 open,就先丢到队列里,等 open 后 flush
|
|
206
|
+
voiceSendQueue.push(view);
|
|
207
|
+
voiceSendQueueBytes += view.byteLength;
|
|
208
|
+
|
|
209
|
+
// 防止队列无限增长:丢弃旧数据,优先保证“实时性”而不是“追赶历史”
|
|
210
|
+
if (voiceSendQueueBytes > VOICE_MAX_QUEUE_BYTES) {
|
|
211
|
+
const drop = Math.floor(voiceSendQueue.length / 2);
|
|
212
|
+
let droppedBytes = 0;
|
|
213
|
+
for (let i = 0; i < drop; i++) {
|
|
214
|
+
droppedBytes += voiceSendQueue[i]?.byteLength || 0;
|
|
215
|
+
}
|
|
216
|
+
voiceSendQueue.splice(0, drop);
|
|
217
|
+
voiceSendQueueBytes -= droppedBytes;
|
|
218
|
+
|
|
219
|
+
if (DEBUG_ASR) {
|
|
220
|
+
console.warn("ASR 队列过大,已丢弃部分旧音频以恢复实时性");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// STEP 4: 创建 VoiceWS 连接
|
|
226
|
+
const createVoiceWS = () => {
|
|
227
|
+
const tm = +new Date();
|
|
228
|
+
const appKey = props.appKey;
|
|
229
|
+
const secret = props.secret;
|
|
230
|
+
|
|
231
|
+
console.log('createVoiceWS',appKey,secret) //3k5jkbo5jsngmjmij7fy77xyrcoud5xkinbaqnik 01a1fe93e03bbfb775ad1a6482a130c2
|
|
232
|
+
|
|
233
|
+
if (!appKey || !secret) {
|
|
234
|
+
console.error("缺少 ASR 配置:appKey/secret");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const sign = sha256(`${appKey}${tm}${secret}`).toUpperCase();
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
// 关闭旧连接
|
|
242
|
+
try {
|
|
243
|
+
voiceWs?.close();
|
|
244
|
+
} catch (_) {}
|
|
245
|
+
|
|
246
|
+
voiceWs = new WebSocket(
|
|
247
|
+
`wss://ws-osasr.hivoice.cn/v1/asr?appkey=${appKey}&time=${tm}&sign=${sign}`
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
voiceWs.onopen = () => {
|
|
251
|
+
if (!voiceWs) return;
|
|
252
|
+
|
|
253
|
+
startVoiceFlushLoop();
|
|
254
|
+
|
|
255
|
+
// 发送 start
|
|
256
|
+
voiceWs.send(
|
|
257
|
+
JSON.stringify({
|
|
258
|
+
type: "start",
|
|
259
|
+
sha: "256",
|
|
260
|
+
data: {
|
|
261
|
+
lang: "cn",
|
|
262
|
+
appkey: appKey,
|
|
263
|
+
userId: "demoUser",
|
|
264
|
+
udid: "demoWebDevice",
|
|
265
|
+
},
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// open 后尽快把队列里的音频 flush 出去
|
|
270
|
+
flushVoiceQueue(true);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
voiceWs.onmessage = (evt: MessageEvent) => {
|
|
274
|
+
let res: any;
|
|
275
|
+
try {
|
|
276
|
+
res = typeof evt.data === "string" ? JSON.parse(evt.data) : evt.data;
|
|
277
|
+
} catch (e) {
|
|
278
|
+
if (DEBUG_ASR) console.warn("ASR 返回非 JSON:", evt.data);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (DEBUG_ASR) {
|
|
283
|
+
console.log("ASR onmessage", res);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 服务端错误码
|
|
287
|
+
if (typeof res?.code === "number" && res.code !== 0) {
|
|
288
|
+
console.warn("ASR 错误:", res);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 实时文本:有些返回只有 showText(中间态),最终态可能在 text 字段
|
|
293
|
+
const chunkText = (res?.showText ?? res?.text ?? "") as string;
|
|
294
|
+
if (chunkText) {
|
|
295
|
+
messageContent.value = chunkText;
|
|
296
|
+
|
|
297
|
+
if (msgArr.length > 0 && msgArr[msgArr.length - 1]?.type !== "fixed") {
|
|
298
|
+
msgArr[msgArr.length - 1] = res;
|
|
299
|
+
} else {
|
|
300
|
+
msgArr.push(res);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
result.value = [...msgArr];
|
|
304
|
+
|
|
305
|
+
// UI:优先展示 showText(更像实时流),没有则回退 text
|
|
306
|
+
inputText.value = result.value
|
|
307
|
+
.map((v: any) => v?.showText ?? v?.text ?? "")
|
|
308
|
+
.join("");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// 结束判断:只 parse 一次,直接读 res.end
|
|
312
|
+
if (res?.end === true) {
|
|
313
|
+
textLoading.value = "finish";
|
|
314
|
+
|
|
315
|
+
const finalText = result.value
|
|
316
|
+
.map((v: any) => v?.text ?? v?.showText ?? "")
|
|
317
|
+
.join("");
|
|
318
|
+
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
inputText.value = finalText;
|
|
321
|
+
messageContent.value = finalText;
|
|
322
|
+
// 收到 end 后可以主动关闭 ASR ws(避免继续占用资源)
|
|
323
|
+
stopVoiceFlushLoop();
|
|
324
|
+
}, 500);
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
voiceWs?.close();
|
|
328
|
+
} catch (_) {}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
voiceWs.onerror = (e) => {
|
|
333
|
+
if (DEBUG_ASR) console.warn("ASR ws error", e);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
voiceWs.onclose = () => {
|
|
337
|
+
stopVoiceFlushLoop();
|
|
338
|
+
voiceSendQueue.length = 0;
|
|
339
|
+
voiceSendQueueBytes = 0;
|
|
340
|
+
voiceWs = null;
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// STEP 5: 启动 VoiceWS 发送循环
|
|
345
|
+
function startVoiceFlushLoop() {
|
|
346
|
+
if (voiceFlushTimer) return;
|
|
347
|
+
voiceFlushTimer = window.setInterval(() => {
|
|
348
|
+
flushVoiceQueue(false);
|
|
349
|
+
}, VOICE_FLUSH_INTERVAL_MS);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// STEP 6: 刷新 VoiceWS 发送队列
|
|
353
|
+
function flushVoiceQueue(force: boolean) {
|
|
354
|
+
if (!voiceWs || voiceWs.readyState !== WebSocket.OPEN) return; // 确保 ws 已打开
|
|
355
|
+
|
|
356
|
+
if (voiceSendQueueBytes <= 0) return;
|
|
357
|
+
|
|
358
|
+
// 背压:如果浏览器 ws 缓冲堆积,就先不 flush(否则延迟会越积越大)
|
|
359
|
+
if (!force && voiceWs.bufferedAmount > VOICE_MAX_BUFFERED_AMOUNT) return;
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
// 直接发 Blob,避免手动拼接拷贝
|
|
363
|
+
const blob = new Blob(voiceSendQueue as any);
|
|
364
|
+
voiceWs.send(blob);
|
|
365
|
+
|
|
366
|
+
voiceSendQueue.length = 0;
|
|
367
|
+
voiceSendQueueBytes = 0;
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.error("flushVoiceQueue 发送失败:", e);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
//! 停止录音按钮
|
|
374
|
+
const stopRecordAudio = () => {
|
|
375
|
+
if (!setRecording.value) return;
|
|
376
|
+
endRecording();
|
|
377
|
+
};
|
|
378
|
+
function endRecording() {
|
|
379
|
+
clearInterval(intervalHandler);
|
|
380
|
+
setRecording.value = false;
|
|
381
|
+
ctime.value = 0;
|
|
382
|
+
|
|
383
|
+
// 先停采集,再 flush + end
|
|
384
|
+
try {
|
|
385
|
+
recorder?.stop();
|
|
386
|
+
} catch (_) {}
|
|
387
|
+
|
|
388
|
+
textLoading.value = "loading";
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
sendAsrEnd();
|
|
392
|
+
} catch (e) {
|
|
393
|
+
console.error("发送 ASR end 失败:", e);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// 发送 ASR end 标记
|
|
397
|
+
function sendAsrEnd() {
|
|
398
|
+
if (!voiceWs || voiceWs.readyState !== WebSocket.OPEN) return;
|
|
399
|
+
|
|
400
|
+
// 确保把最后一点队列 flush 出去再发 end
|
|
401
|
+
flushVoiceQueue(true);
|
|
402
|
+
|
|
403
|
+
voiceWs.send(
|
|
404
|
+
JSON.stringify({
|
|
405
|
+
type: "end",
|
|
406
|
+
})
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
function stopVoiceFlushLoop() {
|
|
410
|
+
if (!voiceFlushTimer) return;
|
|
411
|
+
clearInterval(voiceFlushTimer);
|
|
412
|
+
voiceFlushTimer = null;
|
|
413
|
+
}
|
|
414
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vue-tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"vue": "^3.5.24"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^24.10.1",
|
|
16
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
17
|
+
"@vue/tsconfig": "^0.8.1",
|
|
18
|
+
"typescript": "~5.9.3",
|
|
19
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
20
|
+
"vue-tsc": "^3.1.4"
|
|
21
|
+
},
|
|
22
|
+
"pnpm": {
|
|
23
|
+
"overrides": {
|
|
24
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vue-tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"vue": "^3.5.24"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^24.10.1",
|
|
16
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
17
|
+
"@vue/tsconfig": "^0.8.1",
|
|
18
|
+
"typescript": "~5.9.3",
|
|
19
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
20
|
+
"vue-tsc": "^3.1.4"
|
|
21
|
+
},
|
|
22
|
+
"pnpm": {
|
|
23
|
+
"overrides": {
|
|
24
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vue-tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"vue": "^3.5.24"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^24.10.1",
|
|
16
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
17
|
+
"@vue/tsconfig": "^0.8.1",
|
|
18
|
+
"typescript": "~5.9.3",
|
|
19
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
20
|
+
"vue-tsc": "^3.1.4"
|
|
21
|
+
},
|
|
22
|
+
"pnpm": {
|
|
23
|
+
"overrides": {
|
|
24
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"build": "vue-tsc -b && vite build",
|
|
11
|
+
"preview": "vite preview"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"js-sha256": "^0.11.1",
|
|
15
|
+
"vue": "^3.5.24"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^24.10.1",
|
|
19
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
20
|
+
"@vue/tsconfig": "^0.8.1",
|
|
21
|
+
"typescript": "~5.9.3",
|
|
22
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
23
|
+
"vue-tsc": "^3.1.4"
|
|
24
|
+
},
|
|
25
|
+
"pnpm": {
|
|
26
|
+
"overrides": {
|
|
27
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"build": "vue-tsc -b && vite build",
|
|
11
|
+
"preview": "vite preview"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"js-sha256": "^0.11.1",
|
|
15
|
+
"vue": "^3.5.24"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^24.10.1",
|
|
19
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
20
|
+
"@vue/tsconfig": "^0.8.1",
|
|
21
|
+
"typescript": "~5.9.3",
|
|
22
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
23
|
+
"vue-tsc": "^3.1.4"
|
|
24
|
+
},
|
|
25
|
+
"pnpm": {
|
|
26
|
+
"overrides": {
|
|
27
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"build": "vite build",
|
|
11
|
+
"preview": "vite preview"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"js-sha256": "^0.11.1",
|
|
15
|
+
"vue": "^3.5.24"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^24.10.1",
|
|
19
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
20
|
+
"@vue/tsconfig": "^0.8.1",
|
|
21
|
+
"typescript": "~5.9.3",
|
|
22
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
23
|
+
"vue-tsc": "^3.1.4"
|
|
24
|
+
},
|
|
25
|
+
"pnpm": {
|
|
26
|
+
"overrides": {
|
|
27
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "dist/sur-voice.umd.js ",
|
|
8
|
+
"module": "dist/sur-voice.es.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "vite",
|
|
11
|
+
"build": "vite build",
|
|
12
|
+
"preview": "vite preview"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"js-sha256": "^0.11.1",
|
|
16
|
+
"vue": "^3.5.24"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^24.10.1",
|
|
20
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
21
|
+
"@vue/tsconfig": "^0.8.1",
|
|
22
|
+
"typescript": "~5.9.3",
|
|
23
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
24
|
+
"vue-tsc": "^3.1.4"
|
|
25
|
+
},
|
|
26
|
+
"pnpm": {
|
|
27
|
+
"overrides": {
|
|
28
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "dist/sur-voice.umd.js ",
|
|
8
|
+
"module": "dist/sur-voice.es.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "vite",
|
|
11
|
+
"build": "vite build",
|
|
12
|
+
"preview": "vite preview"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"js-sha256": "^0.11.1",
|
|
16
|
+
"vue": "^3.5.24"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^24.10.1",
|
|
20
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
21
|
+
"@vue/tsconfig": "^0.8.1",
|
|
22
|
+
"typescript": "~5.9.3",
|
|
23
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
24
|
+
"vue-tsc": "^3.1.4"
|
|
25
|
+
},
|
|
26
|
+
"pnpm": {
|
|
27
|
+
"overrides": {
|
|
28
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sur-voice",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "流式语音转文字",
|
|
7
|
+
"main": "dist/sur-voice.umd.js",
|
|
8
|
+
"module": "dist/sur-voice.es.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "vite",
|
|
11
|
+
"build": "vite build",
|
|
12
|
+
"preview": "vite preview"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"js-sha256": "^0.11.1",
|
|
16
|
+
"vue": "^3.5.24"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^24.10.1",
|
|
20
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
21
|
+
"@vue/tsconfig": "^0.8.1",
|
|
22
|
+
"typescript": "~5.9.3",
|
|
23
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
24
|
+
"vue-tsc": "^3.1.4"
|
|
25
|
+
},
|
|
26
|
+
"pnpm": {
|
|
27
|
+
"overrides": {
|
|
28
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|