diff --git a/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx b/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx index 59b28c3..76fdbab 100644 --- a/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx +++ b/sundynix-desktop/frontend/src/shell/JarvisSettings.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Dialog } from "../ui/Dialog"; import { Button } from "../ui/Button"; import { useToast } from "../ui/Toast"; @@ -15,19 +15,31 @@ import { // 每用户 JARVIS 设置:名字 / 人设 / (高级)自带豆包配置。 // 名字与人设归用户自己;豆包配置齐全则语音走用户的账号,否则走系统兜底。 -export function JarvisSettings({ open, onClose }: { open: boolean; onClose: () => void }) { +export function JarvisSettings({ + open, + onClose, + onSaved, +}: { + open: boolean; + onClose: () => void; + onSaved?: () => void; // 保存成功后通知外层刷新(名字要立刻反映到语音坞/HUD,否则看不出生效) +}) { const toast = useToast(); const [cfg, setCfg] = useState(null); + const toastRef = useRef(toast); // effect 里用 ref 取最新 toast,避免它进依赖 + toastRef.current = toast; const [saving, setSaving] = useState(false); const [advanced, setAdvanced] = useState(false); + // 只在"打开"时拉一次。toast 不能进依赖——它一变就会重拉配置、把用户正在填的内容覆盖掉。 useEffect(() => { if (!open) return; setCfg(null); getMyJarvis() .then(setCfg) - .catch((e) => toast.push("error", (e as Error).message)); - }, [open, toast]); + .catch((e) => toastRef.current.push("error", (e as Error).message)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [open]); const set = (k: keyof JarvisConfig, v: string) => setCfg((c) => (c ? { ...c, [k]: v } : c)); @@ -43,7 +55,8 @@ export function JarvisSettings({ open, onClose }: { open: boolean; onClose: () = tts_resource_id: cfg.tts_resource_id, tts_voice_type: cfg.tts_voice_type, }); - toast.push("success", "已保存,下次说话即生效"); + toast.push("success", `已保存:${cfg.name.trim() || "JARVIS"} · 下次说话即生效`); + onSaved?.(); onClose(); } catch (e) { toast.push("error", (e as Error).message); @@ -72,6 +85,15 @@ export function JarvisSettings({ open, onClose }: { open: boolean; onClose: () =
加载中…
) : (
+
+ 当前生效:助手名 {cfg.name.trim() || "JARVIS"} + {" · "}人设 {cfg.persona.trim() ? "已自定义" : "默认平和"} + {" · "}语音走 {cfg.has_own_voice ? "你自己的豆包配置" : "系统配置"} + + 验证是否生效:保存后按住空格问一句「你是谁」,它会用这个名字自称。 + +
+
- setSettingsOpen(false)} /> + setSettingsOpen(false)} onSaved={refreshName} /> {fullscreen && ( setToasts((t) => t.filter((x) => x.id !== id)); + // 必须 useMemo:`value={{ push }}` 每次渲染都造新对象,凡是把 useToast() 结果放进 + // useEffect 依赖的组件(有 7 处)都会在**任意一条 toast 弹出时**重跑 effect。 + // JarvisSettings 因此边填边被重新拉取覆盖;拉取失败还会 push 错误 toast → 无限循环狂闪。 + const value = useMemo(() => ({ push }), [push]); + return ( - + {children}
{toasts.map((t) => {