Skip to content

Commit 8a786b6

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/main'
2 parents f5d215f + e86c998 commit 8a786b6

13 files changed

+790
-31
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ node_modules
1111
dist
1212
dist-ssr
1313
*.local
14+
tx-example*
1415

1516
# Editor directories and files
1617
.vscode/*

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"axios": "^1.7.7",
3838
"class-variance-authority": "^0.7.0",
3939
"clsx": "^2.1.1",
40+
"crypt-js": "^1.0.4",
4041
"date-fns": "^4.1.0",
4142
"dexie": "^4.0.8",
4243
"face-api.js": "^0.22.2",

pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/src/main.rs

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ mod http_client;
1212
use crate::logger::setup_logging;
1313
use crate::device_manager::DeviceManager;
1414
use std::sync::Arc;
15-
use tauri::Manager;
1615

1716
#[tokio::main]
1817
async fn main() -> Result<(), Box<dyn std::error::Error>> {

src-tauri/tauri.conf.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"build": {
44
"beforeDevCommand": "pnpm dev",
55
"beforeBuildCommand": "pnpm build",
6-
"devPath": "http://localhost:1420",
6+
"devPath": "http://localhost:1421",
77
"distDir": "../dist"
88
},
99
"package": {

src/app/api/startVoiceChat/route.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextResponse } from 'next/server';
2+
3+
export async function POST(request: Request) {
4+
try {
5+
const body = await request.json();
6+
7+
// 调用火山引擎的语音识别 API
8+
const response = await fetch('https://rtc.volcengineapi.com', {
9+
method: 'POST',
10+
headers: {
11+
'Content-Type': 'application/json',
12+
// 添加其他必要的认证头
13+
},
14+
body: JSON.stringify(body)
15+
});
16+
17+
const data = await response.json();
18+
return NextResponse.json(data);
19+
20+
} catch (error) {
21+
return NextResponse.json(
22+
{ error: 'Failed to start voice chat' },
23+
{ status: 500 }
24+
);
25+
}
26+
}

src/app/api/stopVoiceChat/route.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextResponse } from 'next/server';
2+
3+
export async function POST(request: Request) {
4+
try {
5+
const { taskId } = await request.json();
6+
7+
// 调用火山引擎的API停止语音识别任务
8+
const response = await fetch('https://rtc.volcengineapi.com/stop', {
9+
method: 'POST',
10+
headers: {
11+
'Content-Type': 'application/json',
12+
// 添加其他必要的认证头
13+
},
14+
body: JSON.stringify({ taskId })
15+
});
16+
17+
const data = await response.json();
18+
return NextResponse.json(data);
19+
20+
} catch (error) {
21+
return NextResponse.json(
22+
{ error: 'Failed to stop voice chat' },
23+
{ status: 500 }
24+
);
25+
}
26+
}

src/components/InteractionInterface.tsx

+10-26
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ import {
2828
AlertDialogTitle,
2929
} from "@/components/ui/alert-dialog";
3030
import { generateBytedanceSpeech } from '@/lib/bytedanceTts';
31-
import { messageQueueService } from '../lib/messageQueueService';
32-
3331

3432
const ModelName = "InteractionInterface";
3533

@@ -66,6 +64,8 @@ export const InteractionInterface: React.FC = () => {
6664
const [selectedVoice, setSelectedVoice] = useState<string>(defaultVoices[0].id);
6765
const [isVoiceCloningOpen, setIsVoiceCloningOpen] = useState(false);
6866
const [voiceToDelete, setVoiceToDelete] = useState<string | null>(null);
67+
const [isVoiceRecognitionEnabled, setIsVoiceRecognitionEnabled] = useState(false);
68+
const [isRecording, setIsRecording] = useState(false);
6969

7070
useEffect(() => {
7171
const loadVoices = async () => {
@@ -281,30 +281,6 @@ export const InteractionInterface: React.FC = () => {
281281
}
282282
};
283283

284-
const handleSendToApp = async () => {
285-
try {
286-
logger.log(`Starting app submission with prompt: ${prompt}`, 'INFO', ModelName);
287-
288-
const result = await generateResponse(prompt);
289-
logger.log(`Generated response: ${JSON.stringify(result)}`, 'INFO', ModelName);
290-
setResponse(result);
291-
292-
const audioBuffer = await generateAudioForResponse(result.response, selectedVoice);
293-
logger.log(`Generated speech buffer size: ${audioBuffer.byteLength}`, 'DEBUG', ModelName);
294-
setAudioBuffer(audioBuffer);
295-
296-
// 直接使用 ArrayBuffer 发送
297-
await messageQueueService.addMessage(
298-
result.kaomoji || 'neutral',
299-
audioBuffer
300-
);
301-
logger.log('Message added to app queue successfully', 'INFO', ModelName);
302-
303-
} catch (error) {
304-
logger.log(`Error in app submission: ${error}`, 'ERROR', ModelName);
305-
}
306-
};
307-
308284
return (
309285
<div className="container max-w-[1100px] mx-auto p-6 space-y-8">
310286
<Card>
@@ -345,6 +321,14 @@ export const InteractionInterface: React.FC = () => {
345321
))}
346322
</SelectContent>
347323
</Select>
324+
<div className="flex items-center gap-2">
325+
<label className="text-sm">语音识别</label>
326+
<Switch
327+
checked={isVoiceRecognitionEnabled}
328+
onCheckedChange={setIsVoiceRecognitionEnabled}
329+
/>
330+
{isRecording && <span className="text-sm text-green-500">录音中...</span>}
331+
</div>
348332
</div>
349333
</CardHeader>
350334
<CardContent>

src/components/SettingsPanel.tsx

+98-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { db } from '../lib/db';
99
import { useSerialPorts } from '../hooks/useSerialPorts';
1010
import { RefreshCw } from 'lucide-react';
1111
import { invoke } from '@tauri-apps/api';
12+
import { Label } from "@/components/ui/label";
1213

1314
interface Device {
1415
deviceId: string;
@@ -38,6 +39,12 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ setIsConfigured })
3839
bytedance_tts_appId: '',
3940
bytedance_tts_token: '',
4041
bytedance_tts_cluster: '',
42+
bytedance_tts_voiceAppId: '',
43+
bytedance_tts_voiceAccessToken: '',
44+
bytedance_tts_asrAppId: '',
45+
tencent_asr_appId: '',
46+
tencent_asr_secretId: '',
47+
tencent_asr_secretKey: ''
4148
});
4249

4350
const [devices, setDevices] = useState<Device[]>([]);
@@ -78,7 +85,29 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ setIsConfigured })
7885
}));
7986
};
8087

81-
const saveSettings = async () => {
88+
const validateVoiceSettings = () => {
89+
const { bytedance_tts_voiceAppId, bytedance_tts_voiceAccessToken } = settings;
90+
const missingFields = [];
91+
92+
if (!bytedance_tts_voiceAppId) {
93+
missingFields.push('语音应用ID');
94+
}
95+
if (!bytedance_tts_voiceAccessToken) {
96+
missingFields.push('访问令牌');
97+
}
98+
99+
if (missingFields.length > 0) {
100+
alert(`请完善以下语音识别设置:\n${missingFields.join('\n')}`);
101+
return false;
102+
}
103+
return true;
104+
};
105+
106+
const handleSave = async () => {
107+
if (!validateVoiceSettings()) {
108+
console.error('Voice recognition settings are incomplete');
109+
return;
110+
}
82111
const settingsToSave = Object.entries(settings).map(([key, value]) => ({
83112
key,
84113
value
@@ -151,6 +180,8 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ setIsConfigured })
151180
<TabsTrigger value="device">设备设置</TabsTrigger>
152181
<TabsTrigger value="tts">TTS设置</TabsTrigger>
153182
<TabsTrigger value="bytedance_tts">ByteDance TTS设置</TabsTrigger>
183+
<TabsTrigger value="bytedance_shibie">ByteDance 实时语音识别设置</TabsTrigger>
184+
<TabsTrigger value="tentcent_asr">腾讯云 实时语音识别设置</TabsTrigger>
154185
</TabsList>
155186
<TabsContent value="api">
156187
<Card>
@@ -344,6 +375,7 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ setIsConfigured })
344375
<div>
345376
<label className="block mb-1">Token</label>
346377
<Input
378+
type="password"
347379
value={settings.bytedance_tts_token}
348380
onChange={(e) => handleChange('bytedance_tts_token', e.target.value)}
349381
/>
@@ -355,12 +387,76 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ setIsConfigured })
355387
onChange={(e) => handleChange('bytedance_tts_cluster', e.target.value)}
356388
/>
357389
</div>
390+
391+
</div>
392+
</CardContent>
393+
</Card>
394+
</TabsContent>
395+
<TabsContent value="bytedance_shibie">
396+
<Card>
397+
<CardHeader>ByteDance 实时语音识别设置</CardHeader>
398+
<CardContent>
399+
<div className="space-y-4">
400+
<div className="pt-6">
401+
<div className="space-y-4">
402+
<div>
403+
<Label htmlFor="voiceAppId">语音应用ID</Label>
404+
<Input
405+
id="voiceAppId"
406+
value={settings.bytedance_tts_voiceAppId}
407+
onChange={(e) => handleChange('bytedance_tts_voiceAppId', e.target.value)}
408+
placeholder="输入语音应用ID"
409+
/>
410+
</div>
411+
<div>
412+
<Label htmlFor="voiceAccessToken">访问令牌</Label>
413+
<Input
414+
id="voiceAccessToken"
415+
value={settings.bytedance_tts_voiceAccessToken}
416+
onChange={(e) => handleChange('bytedance_tts_voiceAccessToken', e.target.value)}
417+
placeholder="输入访问令牌"
418+
/>
419+
</div>
420+
</div>
421+
</div>
422+
</div>
423+
</CardContent>
424+
</Card>
425+
</TabsContent>
426+
<TabsContent value="tentcent_asr">
427+
<Card>
428+
<CardHeader>腾讯云 实时语音识别设置</CardHeader>
429+
<CardContent>
430+
<div className="space-y-4">
431+
<div>
432+
<label className="block mb-1">App ID</label>
433+
<Input
434+
value={settings.tencent_asr_appId}
435+
onChange={(e) => handleChange('tencent_asr_appId', e.target.value)}
436+
/>
437+
</div>
438+
<div>
439+
<label className="block mb-1">Secret ID</label>
440+
<Input
441+
value={settings.tencent_asr_secretId}
442+
onChange={(e) => handleChange('tencent_asr_secretId', e.target.value)}
443+
/>
444+
</div>
445+
<div>
446+
<label className="block mb-1">Secret Key</label>
447+
<Input
448+
type="password"
449+
value={settings.tencent_asr_secretKey}
450+
onChange={(e) => handleChange('tencent_asr_secretKey', e.target.value)}
451+
/>
452+
</div>
358453
</div>
454+
359455
</CardContent>
360456
</Card>
361457
</TabsContent>
362458
</Tabs>
363-
<Button onClick={saveSettings} className="mt-4">保存所有设置</Button>
459+
<Button onClick={handleSave} className="mt-4">保存所有设置</Button>
364460
</div>
365461
);
366462
};

0 commit comments

Comments
 (0)