feat: friend text tts ok but websearch tts not ok
Browse files
bun.lock
CHANGED
|
@@ -77,6 +77,7 @@
|
|
| 77 |
"lodash-es": "^4.17.23",
|
| 78 |
"lru-cache": "^11.2.7",
|
| 79 |
"marked": "^17.0.5",
|
|
|
|
| 80 |
"p-map": "^7.0.4",
|
| 81 |
"picomatch": "^4.0.4",
|
| 82 |
"plist": "^3.1.0",
|
|
@@ -875,6 +876,8 @@
|
|
| 875 |
|
| 876 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
| 877 |
|
|
|
|
|
|
|
| 878 |
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
| 879 |
|
| 880 |
"node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
|
|
@@ -1381,6 +1384,10 @@
|
|
| 1381 |
|
| 1382 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1384 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1385 |
|
| 1386 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
|
@@ -1475,6 +1482,14 @@
|
|
| 1475 |
|
| 1476 |
"ink/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
| 1477 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1478 |
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
| 1479 |
|
| 1480 |
"qrcode/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
@@ -1525,6 +1540,16 @@
|
|
| 1525 |
|
| 1526 |
"gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1527 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1528 |
"qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1529 |
|
| 1530 |
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
@@ -1545,6 +1570,12 @@
|
|
| 1545 |
|
| 1546 |
"@grpc/proto-loader/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1548 |
"qrcode/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1549 |
|
| 1550 |
"qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
|
|
| 77 |
"lodash-es": "^4.17.23",
|
| 78 |
"lru-cache": "^11.2.7",
|
| 79 |
"marked": "^17.0.5",
|
| 80 |
+
"node-edge-tts": "^1.2.10",
|
| 81 |
"p-map": "^7.0.4",
|
| 82 |
"picomatch": "^4.0.4",
|
| 83 |
"plist": "^3.1.0",
|
|
|
|
| 876 |
|
| 877 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
| 878 |
|
| 879 |
+
"node-edge-tts": ["node-edge-tts@1.2.10", "", { "dependencies": { "https-proxy-agent": "^7.0.1", "ws": "^8.13.0", "yargs": "^17.7.2" }, "bin": { "node-edge-tts": "bin.js" } }, "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ=="],
|
| 880 |
+
|
| 881 |
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
| 882 |
|
| 883 |
"node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
|
|
|
|
| 1384 |
|
| 1385 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1386 |
|
| 1387 |
+
"node-edge-tts/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
| 1388 |
+
|
| 1389 |
+
"node-edge-tts/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
| 1390 |
+
|
| 1391 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1392 |
|
| 1393 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
|
|
|
| 1482 |
|
| 1483 |
"ink/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
| 1484 |
|
| 1485 |
+
"node-edge-tts/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1486 |
+
|
| 1487 |
+
"node-edge-tts/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
| 1488 |
+
|
| 1489 |
+
"node-edge-tts/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
| 1490 |
+
|
| 1491 |
+
"node-edge-tts/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
| 1492 |
+
|
| 1493 |
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
| 1494 |
|
| 1495 |
"qrcode/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
|
|
| 1540 |
|
| 1541 |
"gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1542 |
|
| 1543 |
+
"node-edge-tts/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1544 |
+
|
| 1545 |
+
"node-edge-tts/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
| 1546 |
+
|
| 1547 |
+
"node-edge-tts/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
| 1548 |
+
|
| 1549 |
+
"node-edge-tts/yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
| 1550 |
+
|
| 1551 |
+
"node-edge-tts/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1552 |
+
|
| 1553 |
"qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1554 |
|
| 1555 |
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
|
|
| 1570 |
|
| 1571 |
"@grpc/proto-loader/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1572 |
|
| 1573 |
+
"node-edge-tts/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1574 |
+
|
| 1575 |
+
"node-edge-tts/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
| 1576 |
+
|
| 1577 |
+
"node-edge-tts/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1578 |
+
|
| 1579 |
"qrcode/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1580 |
|
| 1581 |
"qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
package.json
CHANGED
|
@@ -95,6 +95,7 @@
|
|
| 95 |
"lodash-es": "^4.17.23",
|
| 96 |
"lru-cache": "^11.2.7",
|
| 97 |
"marked": "^17.0.5",
|
|
|
|
| 98 |
"p-map": "^7.0.4",
|
| 99 |
"picomatch": "^4.0.4",
|
| 100 |
"plist": "^3.1.0",
|
|
|
|
| 95 |
"lodash-es": "^4.17.23",
|
| 96 |
"lru-cache": "^11.2.7",
|
| 97 |
"marked": "^17.0.5",
|
| 98 |
+
"node-edge-tts": "^1.2.10",
|
| 99 |
"p-map": "^7.0.4",
|
| 100 |
"picomatch": "^4.0.4",
|
| 101 |
"plist": "^3.1.0",
|
src/components/friend/frontend/components/ChatInput.tsx
CHANGED
|
@@ -196,12 +196,20 @@ export function ChatInput({ visible = true, onActiveChange, uiAlign = 'right', o
|
|
| 196 |
}, [handleVoiceCallResult, sttProvider, serverStt, language])
|
| 197 |
|
| 198 |
// --- Voice Call: end ---
|
| 199 |
-
const endVoiceCall = useCallback(() => {
|
|
|
|
| 200 |
voiceCallActiveRef.current = false
|
| 201 |
setVoiceCallActive(false)
|
| 202 |
-
serverStt.stopStreaming()
|
| 203 |
setRecording(false)
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
if (silenceTimerRef.current) { clearTimeout(silenceTimerRef.current); silenceTimerRef.current = null }
|
| 206 |
cancelInterrupt()
|
| 207 |
mutedUntilRef.current = 0
|
|
@@ -209,7 +217,7 @@ export function ChatInput({ visible = true, onActiveChange, uiAlign = 'right', o
|
|
| 209 |
setText('')
|
| 210 |
setOpen(false)
|
| 211 |
onActiveChange?.(false)
|
| 212 |
-
}, [onActiveChange, cancelInterrupt, serverStt])
|
| 213 |
|
| 214 |
endVoiceCallRef.current = endVoiceCall
|
| 215 |
|
|
|
|
| 196 |
}, [handleVoiceCallResult, sttProvider, serverStt, language])
|
| 197 |
|
| 198 |
// --- Voice Call: end ---
|
| 199 |
+
const endVoiceCall = useCallback(async () => {
|
| 200 |
+
if (!voiceCallActiveRef.current) return // guard against double-invocation
|
| 201 |
voiceCallActiveRef.current = false
|
| 202 |
setVoiceCallActive(false)
|
|
|
|
| 203 |
setRecording(false)
|
| 204 |
|
| 205 |
+
// Stop capture and get the final transcript
|
| 206 |
+
const text = await serverStt.stopStreaming()
|
| 207 |
+
|
| 208 |
+
// Send any remaining transcript as a message (no-op if empty)
|
| 209 |
+
if (text.trim()) {
|
| 210 |
+
voiceCallSend(text)
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
if (silenceTimerRef.current) { clearTimeout(silenceTimerRef.current); silenceTimerRef.current = null }
|
| 214 |
cancelInterrupt()
|
| 215 |
mutedUntilRef.current = 0
|
|
|
|
| 217 |
setText('')
|
| 218 |
setOpen(false)
|
| 219 |
onActiveChange?.(false)
|
| 220 |
+
}, [onActiveChange, cancelInterrupt, serverStt, voiceCallSend])
|
| 221 |
|
| 222 |
endVoiceCallRef.current = endVoiceCall
|
| 223 |
|
src/components/friend/frontend/hooks/useServerStt.ts
CHANGED
|
@@ -95,8 +95,11 @@ export function useServerStt() {
|
|
| 95 |
[],
|
| 96 |
)
|
| 97 |
|
| 98 |
-
/**
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
| 100 |
// Stop polling
|
| 101 |
const pollId = (window as any).__friendSttPollId
|
| 102 |
if (pollId) {
|
|
@@ -104,12 +107,12 @@ export function useServerStt() {
|
|
| 104 |
delete (window as any).__friendSttPollId
|
| 105 |
}
|
| 106 |
|
| 107 |
-
// Stop capture
|
| 108 |
-
fetch(`${FRIEND_API_BASE}/voice/stop`, { method: 'POST' })
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
}, [])
|
| 114 |
|
| 115 |
return {
|
|
|
|
| 95 |
[],
|
| 96 |
)
|
| 97 |
|
| 98 |
+
/**
|
| 99 |
+
* Stop streaming STT.
|
| 100 |
+
* Returns the final transcript text from the server.
|
| 101 |
+
*/
|
| 102 |
+
const stopStreaming = useCallback(async (): Promise<string> => {
|
| 103 |
// Stop polling
|
| 104 |
const pollId = (window as any).__friendSttPollId
|
| 105 |
if (pollId) {
|
|
|
|
| 107 |
delete (window as any).__friendSttPollId
|
| 108 |
}
|
| 109 |
|
| 110 |
+
// Stop capture and get final transcript
|
| 111 |
+
const res = await fetch(`${FRIEND_API_BASE}/voice/stop`, { method: 'POST' })
|
| 112 |
+
setConnected(false)
|
| 113 |
+
if (!res.ok) return ''
|
| 114 |
+
const data = await res.json()
|
| 115 |
+
return data.text || ''
|
| 116 |
}, [])
|
| 117 |
|
| 118 |
return {
|
src/friend/FriendService.ts
CHANGED
|
@@ -369,9 +369,10 @@ class FriendService {
|
|
| 369 |
// Generate TTS for the full response if enabled
|
| 370 |
if (prefs.ttsEnabled) {
|
| 371 |
try {
|
| 372 |
-
const
|
| 373 |
-
if (
|
| 374 |
-
|
|
|
|
| 375 |
}
|
| 376 |
} catch (err) {
|
| 377 |
console.warn('[FriendService] TTS generation failed:', err);
|
|
@@ -460,7 +461,7 @@ class FriendService {
|
|
| 460 |
try {
|
| 461 |
const args = tool === 'parecord'
|
| 462 |
? ['--raw', '--rate=16000', '--format=s16le', '--channels=1', '--latency-msec=20']
|
| 463 |
-
: ['-r', '16000', '-f', 'S16_LE', '-c', '1', '-t', 'raw', '-q'];
|
| 464 |
const proc = spawn(tool, args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
| 465 |
if (proc.pid !== undefined) {
|
| 466 |
captureProc = proc;
|
|
|
|
| 369 |
// Generate TTS for the full response if enabled
|
| 370 |
if (prefs.ttsEnabled) {
|
| 371 |
try {
|
| 372 |
+
const audioId = await this.generateTts(text);
|
| 373 |
+
if (audioId) {
|
| 374 |
+
const fullUrl = `http://127.0.0.1:3456/plugins/friend/audio/${audioId}`;
|
| 375 |
+
broadcastToVrm({ audioUrl: fullUrl, sendFirstTts: true });
|
| 376 |
}
|
| 377 |
} catch (err) {
|
| 378 |
console.warn('[FriendService] TTS generation failed:', err);
|
|
|
|
| 461 |
try {
|
| 462 |
const args = tool === 'parecord'
|
| 463 |
? ['--raw', '--rate=16000', '--format=s16le', '--channels=1', '--latency-msec=20']
|
| 464 |
+
: ['-D', 'default', '-r', '16000', '-f', 'S16_LE', '-c', '1', '-t', 'raw', '-q'];
|
| 465 |
const proc = spawn(tool, args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
| 466 |
if (proc.pid !== undefined) {
|
| 467 |
captureProc = proc;
|