From 24351014e02bd34e0fca71ee7eaafd1cd64989c6 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Wed, 22 Jul 2026 14:12:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20JARVIS=20=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E2=80=94=E2=80=94=E5=90=8D=E5=AD=97/?= =?UTF-8?q?=E4=BA=BA=E8=AE=BE/=E8=87=AA=E5=B8=A6=E8=B1=86=E5=8C=85?= =?UTF-8?q?=E9=85=8D=E7=BD=AE(=E7=94=A8=E6=88=B7=E7=BA=A7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lib/api.ts: getMyJarvis/saveMyJarvis + JarvisConfig 类型 - shell/JarvisSettings.tsx: 弹窗——名字 + 语气人设 + 折叠「高级:我的豆包配置」 (api_key/asr/tts resource-id/音色;掩码不回传=沿用已存;填齐才覆盖系统) - VoiceDock: 麦克风上方加设置齿轮,打开面板 真机验证:面板正确加载后端配置(名字=星期五、人设),高级区四项占位清晰,存档下次说话生效。 Co-Authored-By: Claude Opus 4.8 --- sundynix-desktop/frontend/src/lib/api.ts | 35 +++++ .../frontend/src/shell/JarvisSettings.tsx | 139 ++++++++++++++++++ .../frontend/src/shell/VoiceDock.tsx | 17 ++- 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 sundynix-desktop/frontend/src/shell/JarvisSettings.tsx diff --git a/sundynix-desktop/frontend/src/lib/api.ts b/sundynix-desktop/frontend/src/lib/api.ts index a71f173..d8efab9 100644 --- a/sundynix-desktop/frontend/src/lib/api.ts +++ b/sundynix-desktop/frontend/src/lib/api.ts @@ -789,3 +789,38 @@ export async function runReplay(taskId: string): Promise<{ output: string; exec: const d = (await res.json()) as { output?: string; exec?: ExecEvent[] }; return { output: d.output ?? "", exec: d.exec ?? [] }; } + +// ---- 每用户 JARVIS 设置(名字 / 人设 / 自带豆包配置)---- +export interface JarvisConfig { + name: string; + persona: string; + asr_resource_id: string; + tts_resource_id: string; + tts_voice_type: string; + api_key: string; // 读取时为脱敏值;保存留空/掩码=沿用已存 + has_own_voice: boolean; +} + +export async function getMyJarvis(): Promise { + const res = guard401(await fetch(`${GATEWAY}/api/v1/me/jarvis`, { headers: bearer() })); + if (!res.ok) throw new Error("读取 JARVIS 设置失败"); + return res.json() as Promise; +} + +export async function saveMyJarvis(cfg: { + name: string; + persona: string; + api_key?: string; + asr_resource_id?: string; + tts_resource_id?: string; + tts_voice_type?: string; +}): Promise { + const res = guard401( + await fetch(`${GATEWAY}/api/v1/me/jarvis`, { + method: "PUT", + headers: { "Content-Type": "application/json", ...bearer() }, + body: JSON.stringify(cfg), + }), + ); + if (!res.ok) throw new Error(((await res.json().catch(() => ({}))) as { error?: string }).error || "保存失败"); +} diff --git a/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx b/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx new file mode 100644 index 0000000..82fb6a5 --- /dev/null +++ b/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx @@ -0,0 +1,139 @@ +import { useEffect, useState } from "react"; +import { Dialog } from "../ui/Dialog"; +import { Button } from "../ui/Button"; +import { useToast } from "../ui/Toast"; +import { getMyJarvis, saveMyJarvis, type JarvisConfig } from "../lib/api"; + +// 每用户 JARVIS 设置:名字 / 人设 / (高级)自带豆包配置。 +// 名字与人设归用户自己;豆包配置齐全则语音走用户的账号,否则走系统兜底。 +export function JarvisSettings({ open, onClose }: { open: boolean; onClose: () => void }) { + const toast = useToast(); + const [cfg, setCfg] = useState(null); + const [saving, setSaving] = useState(false); + const [advanced, setAdvanced] = useState(false); + + useEffect(() => { + if (!open) return; + setCfg(null); + getMyJarvis() + .then(setCfg) + .catch((e) => toast.push("error", (e as Error).message)); + }, [open, toast]); + + const set = (k: keyof JarvisConfig, v: string) => setCfg((c) => (c ? { ...c, [k]: v } : c)); + + const onSave = async () => { + if (!cfg) return; + setSaving(true); + try { + await saveMyJarvis({ + name: cfg.name, + persona: cfg.persona, + api_key: cfg.api_key.includes("•") ? "" : cfg.api_key, // 掩码=没改动,留空沿用已存 + asr_resource_id: cfg.asr_resource_id, + tts_resource_id: cfg.tts_resource_id, + tts_voice_type: cfg.tts_voice_type, + }); + toast.push("success", "已保存,下次说话即生效"); + onClose(); + } catch (e) { + toast.push("error", (e as Error).message); + } finally { + setSaving(false); + } + }; + + return ( + + + + + } + > + {!cfg ? ( +
加载中…
+ ) : ( +
+ + +