Enhance DebugDrawer and DebugVoicePanel with unsaved changes handling
- Add `hasUnsavedChanges` prop to `DebugDrawer` to indicate unsaved changes in the AssistantPage. - Update `DebugVoicePanel` to display appropriate messages and disable actions when there are unsaved changes. - Refactor related components to ensure consistent handling of unsaved changes across the AssistantPage, improving user experience during interactions.
This commit is contained in:
@@ -1302,6 +1302,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
<DebugDrawer
|
||||
assistantId={editingId}
|
||||
asSheet
|
||||
hasUnsavedChanges={dirty}
|
||||
onNodeActive={setActiveNodeId}
|
||||
/>
|
||||
</SheetContent>
|
||||
@@ -1403,7 +1404,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<DebugDrawer assistantId={editingId} />
|
||||
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1517,7 +1518,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<DebugDrawer assistantId={editingId} />
|
||||
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1642,7 +1643,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<DebugDrawer assistantId={editingId} />
|
||||
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1859,7 +1860,11 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<DebugDrawer assistantId={editingId} vision={visionEnabled} />
|
||||
<DebugDrawer
|
||||
assistantId={editingId}
|
||||
hasUnsavedChanges={dirty}
|
||||
vision={visionEnabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1934,11 +1939,13 @@ function SegmentedIconButton({
|
||||
function DebugDrawer({
|
||||
assistantId,
|
||||
asSheet = false,
|
||||
hasUnsavedChanges = false,
|
||||
onNodeActive,
|
||||
vision = false,
|
||||
}: {
|
||||
assistantId: string | null;
|
||||
asSheet?: boolean;
|
||||
hasUnsavedChanges?: boolean;
|
||||
onNodeActive?: (nodeId: string | null) => void;
|
||||
vision?: boolean;
|
||||
}) {
|
||||
@@ -2050,6 +2057,7 @@ function DebugDrawer({
|
||||
assistantId={assistantId}
|
||||
preview={preview}
|
||||
camera={camera}
|
||||
hasUnsavedChanges={hasUnsavedChanges}
|
||||
vision={vision}
|
||||
/>
|
||||
</aside>
|
||||
@@ -2180,6 +2188,7 @@ function DebugVoicePanel({
|
||||
assistantId,
|
||||
preview,
|
||||
camera,
|
||||
hasUnsavedChanges,
|
||||
vision,
|
||||
}: {
|
||||
view: DebugView;
|
||||
@@ -2188,6 +2197,7 @@ function DebugVoicePanel({
|
||||
assistantId: string | null;
|
||||
preview: VoicePreview;
|
||||
camera: CameraPreview;
|
||||
hasUnsavedChanges: boolean;
|
||||
vision: boolean;
|
||||
}) {
|
||||
const {
|
||||
@@ -2210,6 +2220,12 @@ function DebugVoicePanel({
|
||||
const showIdleHub =
|
||||
idleOrFailed &&
|
||||
(view === "video" || (messages.length === 0 && inChatView));
|
||||
const startBlockedMessage = !assistantId
|
||||
? "请先保存助手,再开始对话。"
|
||||
: hasUnsavedChanges
|
||||
? "请先保存当前改动,再开始对话。"
|
||||
: "";
|
||||
const startDisabled = status === "connecting" || Boolean(startBlockedMessage);
|
||||
|
||||
function handleSendText() {
|
||||
if (sendText(textDraft)) {
|
||||
@@ -2218,12 +2234,13 @@ function DebugVoicePanel({
|
||||
}
|
||||
|
||||
const startConversation = useCallback(async () => {
|
||||
if (!assistantId || hasUnsavedChanges) return;
|
||||
const videoStream = vision ? await camera.start() : null;
|
||||
await connect({
|
||||
visionEnabled: vision,
|
||||
videoStream,
|
||||
});
|
||||
}, [camera, connect, vision]);
|
||||
}, [assistantId, camera, connect, hasUnsavedChanges, vision]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
@@ -2234,7 +2251,7 @@ function DebugVoicePanel({
|
||||
<DebugIdleHub
|
||||
status={status}
|
||||
error={error}
|
||||
assistantId={assistantId}
|
||||
message={startBlockedMessage}
|
||||
connect={startConversation}
|
||||
/>
|
||||
) : (
|
||||
@@ -2245,7 +2262,7 @@ function DebugVoicePanel({
|
||||
<DebugIdleHub
|
||||
status={status}
|
||||
error={error}
|
||||
assistantId={assistantId}
|
||||
message={startBlockedMessage}
|
||||
connect={startConversation}
|
||||
/>
|
||||
) : (
|
||||
@@ -2307,6 +2324,8 @@ function DebugVoicePanel({
|
||||
"连接失败,请确认后端已启动且助手已保存后重试。"
|
||||
: !assistantId
|
||||
? "请先保存助手,再开始语音预览。"
|
||||
: hasUnsavedChanges
|
||||
? "请先保存当前改动,再开始语音预览。"
|
||||
: micWarning
|
||||
? `${micWarning} 可接收助手播报,但无法发送语音。`
|
||||
: recording
|
||||
@@ -2316,7 +2335,7 @@ function DebugVoicePanel({
|
||||
</div>
|
||||
|
||||
<Button
|
||||
disabled={!assistantId || status === "connecting"}
|
||||
disabled={recording ? status === "connecting" : startDisabled}
|
||||
onClick={() => {
|
||||
if (recording) {
|
||||
disconnect();
|
||||
@@ -2403,33 +2422,40 @@ function DebugVoicePanel({
|
||||
</Button>
|
||||
)}
|
||||
{!showIdleHub && (
|
||||
<Button
|
||||
size="sm"
|
||||
disabled={!assistantId || status === "connecting"}
|
||||
onClick={() => {
|
||||
if (recording) {
|
||||
disconnect();
|
||||
} else {
|
||||
void startConversation();
|
||||
}
|
||||
}}
|
||||
className={[
|
||||
"h-10 shrink-0 gap-1.5 rounded-full px-4",
|
||||
recording
|
||||
? "bg-destructive text-white hover:bg-destructive/90"
|
||||
: "",
|
||||
].join(" ")}
|
||||
aria-label={recording ? "结束语音测试" : "开始语音测试"}
|
||||
>
|
||||
{status === "connecting" ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
) : recording ? (
|
||||
<PhoneOff size={14} />
|
||||
) : (
|
||||
<Mic size={14} />
|
||||
<div className="flex shrink-0 flex-col items-end gap-1">
|
||||
<Button
|
||||
size="sm"
|
||||
disabled={recording ? status === "connecting" : startDisabled}
|
||||
onClick={() => {
|
||||
if (recording) {
|
||||
disconnect();
|
||||
} else {
|
||||
void startConversation();
|
||||
}
|
||||
}}
|
||||
className={[
|
||||
"h-10 gap-1.5 rounded-full px-4",
|
||||
recording
|
||||
? "bg-destructive text-white hover:bg-destructive/90"
|
||||
: "",
|
||||
].join(" ")}
|
||||
aria-label={recording ? "结束语音测试" : "开始语音测试"}
|
||||
>
|
||||
{status === "connecting" ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
) : recording ? (
|
||||
<PhoneOff size={14} />
|
||||
) : (
|
||||
<Mic size={14} />
|
||||
)}
|
||||
{recording ? "结束对话" : "开始对话"}
|
||||
</Button>
|
||||
{!recording && startBlockedMessage && (
|
||||
<span className="max-w-40 text-right text-[11px] leading-4 text-destructive">
|
||||
{startBlockedMessage}
|
||||
</span>
|
||||
)}
|
||||
{recording ? "结束对话" : "开始对话"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2449,18 +2475,25 @@ function DebugVoicePanel({
|
||||
// 空闲态只保留一个明确入口,避免中间区域显得拥挤。
|
||||
function DebugIdleHub({
|
||||
status,
|
||||
assistantId,
|
||||
error,
|
||||
message,
|
||||
connect,
|
||||
}: {
|
||||
status: VoicePreviewStatus;
|
||||
error: string | null;
|
||||
assistantId: string | null;
|
||||
message: string;
|
||||
connect: () => Promise<void>;
|
||||
}) {
|
||||
const helperText =
|
||||
message ||
|
||||
(status === "failed"
|
||||
? error || "连接失败,请确认后端已启动且助手已保存后重试。"
|
||||
: "");
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 items-center justify-center px-6 py-8">
|
||||
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 py-8 text-center">
|
||||
<Button
|
||||
disabled={!assistantId}
|
||||
disabled={Boolean(message)}
|
||||
onClick={() => void connect()}
|
||||
className="h-11 gap-2 rounded-full px-6 text-sm font-medium shadow-sm"
|
||||
aria-label={status === "failed" ? "重新连接" : "开始语音测试"}
|
||||
@@ -2468,6 +2501,16 @@ function DebugIdleHub({
|
||||
<Mic size={18} />
|
||||
{status === "failed" ? "重新连接" : "开始对话"}
|
||||
</Button>
|
||||
{helperText && (
|
||||
<p
|
||||
className={[
|
||||
"max-w-xs text-xs leading-5",
|
||||
message ? "text-destructive" : "text-muted-foreground",
|
||||
].join(" ")}
|
||||
>
|
||||
{helperText}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user