fix: add service status indicator for ongoing issues (#95)

This commit is contained in:
Dayuan Jiang
2025-12-05 16:46:17 +09:00
committed by GitHub
parent c805277a76
commit 970b88612d

View File

@@ -4,7 +4,7 @@ import type React from "react";
import { useRef, useEffect, useState } from "react"; import { useRef, useEffect, useState } from "react";
import { flushSync } from "react-dom"; import { flushSync } from "react-dom";
import { FaGithub } from "react-icons/fa"; import { FaGithub } from "react-icons/fa";
import { PanelRightClose, PanelRightOpen } from "lucide-react"; import { PanelRightClose, PanelRightOpen, AlertTriangle } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
@@ -72,8 +72,15 @@ export default function ChatPanel({
chartXMLRef.current = chartXML; chartXMLRef.current = chartXML;
}, [chartXML]); }, [chartXML]);
const { messages, sendMessage, addToolResult, status, error, setMessages, stop } = const {
useChat({ messages,
sendMessage,
addToolResult,
status,
error,
setMessages,
stop,
} = useChat({
transport: new DefaultChatTransport({ transport: new DefaultChatTransport({
api: "/api/chat", api: "/api/chat",
}), }),
@@ -110,12 +117,20 @@ export default function ChatPanel({
const cachedXML = chartXMLRef.current; const cachedXML = chartXMLRef.current;
if (cachedXML) { if (cachedXML) {
currentXml = cachedXML; currentXml = cachedXML;
console.log("[edit_diagram] Using cached chartXML, length:", currentXml.length); console.log(
"[edit_diagram] Using cached chartXML, length:",
currentXml.length
);
} else { } else {
// Fallback to export only if no cached XML // Fallback to export only if no cached XML
console.log("[edit_diagram] No cached XML, fetching from DrawIO..."); console.log(
"[edit_diagram] No cached XML, fetching from DrawIO..."
);
currentXml = await onFetchChart(false); currentXml = await onFetchChart(false);
console.log("[edit_diagram] Got XML from export, length:", currentXml.length); console.log(
"[edit_diagram] Got XML from export, length:",
currentXml.length
);
} }
const { replaceXMLParts } = await import("@/lib/utils"); const { replaceXMLParts } = await import("@/lib/utils");
@@ -133,9 +148,7 @@ export default function ChatPanel({
console.error("[edit_diagram] Failed:", error); console.error("[edit_diagram] Failed:", error);
const errorMessage = const errorMessage =
error instanceof Error error instanceof Error ? error.message : String(error);
? error.message
: String(error);
addToolResult({ addToolResult({
tool: "edit_diagram", tool: "edit_diagram",
@@ -201,9 +214,13 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
messages.length === capturedMessageCount && messages.length === capturedMessageCount &&
newPartsCount === capturedPartsCount newPartsCount === capturedPartsCount
) { ) {
console.error("[Streaming Timeout] No activity for 15s - forcing error state"); console.error(
"[Streaming Timeout] No activity for 15s - forcing error state"
);
setStreamingError( setStreamingError(
new Error("Connection lost. The AI service may be temporarily unavailable. Please try again.") new Error(
"Connection lost. The AI service may be temporarily unavailable. Please try again."
)
); );
stop(); // Allow user to retry by transitioning status to "ready" stop(); // Allow user to retry by transitioning status to "ready"
} }
@@ -220,11 +237,12 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
} }
}, [messages]); }, [messages]);
const onFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => { const onFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
// Allow retry if there's a streaming error (workaround for stop() not transitioning status) // Allow retry if there's a streaming error (workaround for stop() not transitioning status)
const isProcessing = (status === "streaming" || status === "submitted") && !streamingError; const isProcessing =
(status === "streaming" || status === "submitted") &&
!streamingError;
if (input.trim() && !isProcessing) { if (input.trim() && !isProcessing) {
// Clear any previous streaming error before starting new request // Clear any previous streaming error before starting new request
setStreamingError(null); setStreamingError(null);
@@ -292,7 +310,10 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
// Find the user message before this assistant message // Find the user message before this assistant message
let userMessageIndex = messageIndex - 1; let userMessageIndex = messageIndex - 1;
while (userMessageIndex >= 0 && messages[userMessageIndex].role !== "user") { while (
userMessageIndex >= 0 &&
messages[userMessageIndex].role !== "user"
) {
userMessageIndex--; userMessageIndex--;
} }
@@ -308,7 +329,10 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
// Get the saved XML snapshot for this user message // Get the saved XML snapshot for this user message
const savedXml = xmlSnapshotsRef.current.get(userMessageIndex); const savedXml = xmlSnapshotsRef.current.get(userMessageIndex);
if (!savedXml) { if (!savedXml) {
console.error("No saved XML snapshot for message index:", userMessageIndex); console.error(
"No saved XML snapshot for message index:",
userMessageIndex
);
return; return;
} }
@@ -353,7 +377,10 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
// Get the saved XML snapshot for this user message // Get the saved XML snapshot for this user message
const savedXml = xmlSnapshotsRef.current.get(messageIndex); const savedXml = xmlSnapshotsRef.current.get(messageIndex);
if (!savedXml) { if (!savedXml) {
console.error("No saved XML snapshot for message index:", messageIndex); console.error(
"No saved XML snapshot for message index:",
messageIndex
);
return; return;
} }
@@ -447,6 +474,14 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
> >
About About
</Link> </Link>
<ButtonWithTooltip
tooltipContent="We're experiencing issues with diagram generation. Debugging is in progress."
variant="ghost"
size="icon"
className="h-6 w-6 text-amber-500 hover:text-amber-600"
>
<AlertTriangle className="h-4 w-4" />
</ButtonWithTooltip>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<a <a