From aaa56ff004cdc9fcaee82c676d6a54835662f22a Mon Sep 17 00:00:00 2001
From: viettranx
Date: Tue, 31 Mar 2026 23:07:15 +0700
Subject: [PATCH] fix(ui): summoning modal mode, dialog widths, and MCP refresh
animation
- Add mode prop to SummoningModal (summon vs regenerate) so Edit with AI
shows appropriate text instead of summoning language
- Fix memory document and KG entity detail dialogs using sm:max-w-* to
properly override base sm:max-w-lg from DialogContent
- Expose isFetching from useMCP hook so refresh button animation works
---
ui/web/src/i18n/locales/en/agents.json | 7 ++++++-
ui/web/src/i18n/locales/vi/agents.json | 7 ++++++-
ui/web/src/i18n/locales/zh/agents.json | 7 ++++++-
.../agents/agent-detail/agent-detail-page.tsx | 4 ++++
ui/web/src/pages/agents/summoning-modal.tsx | 16 ++++++++++------
ui/web/src/pages/mcp/hooks/use-mcp.ts | 3 ++-
ui/web/src/pages/mcp/mcp-page.tsx | 4 ++--
.../src/pages/memory/kg-entity-detail-dialog.tsx | 2 +-
.../src/pages/memory/memory-document-dialog.tsx | 2 +-
9 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/ui/web/src/i18n/locales/en/agents.json b/ui/web/src/i18n/locales/en/agents.json
index f8b01c2c..8aff8ee3 100644
--- a/ui/web/src/i18n/locales/en/agents.json
+++ b/ui/web/src/i18n/locales/en/agents.json
@@ -533,7 +533,12 @@
"retrying": "Retrying...",
"done": "done",
"fileLabelSOUL": "Soul & Personality",
- "fileLabelIDENTITY": "Identity Card"
+ "fileLabelIDENTITY": "Identity Card",
+ "regenerateTitle": "Editing with AI...",
+ "regenerateCompleted": "Edit Complete!",
+ "regenerateFailed": "Edit Failed",
+ "regenerateWeavingSoul": "Updating files for",
+ "regenerateWait": "AI is rewriting the files. Please wait..."
},
"configGroups": {
"core": "Core",
diff --git a/ui/web/src/i18n/locales/vi/agents.json b/ui/web/src/i18n/locales/vi/agents.json
index 386d0716..16c27939 100644
--- a/ui/web/src/i18n/locales/vi/agents.json
+++ b/ui/web/src/i18n/locales/vi/agents.json
@@ -533,7 +533,12 @@
"retrying": "Đang thử lại...",
"done": "xong",
"fileLabelSOUL": "Linh hồn & Tính cách",
- "fileLabelIDENTITY": "Thẻ danh tính"
+ "fileLabelIDENTITY": "Thẻ danh tính",
+ "regenerateTitle": "Đang chỉnh sửa bằng AI...",
+ "regenerateCompleted": "Chỉnh sửa hoàn tất!",
+ "regenerateFailed": "Chỉnh sửa thất bại",
+ "regenerateWeavingSoul": "Đang cập nhật tệp cho",
+ "regenerateWait": "AI đang viết lại các tệp. Vui lòng chờ..."
},
"configGroups": {
"core": "Cốt lõi",
diff --git a/ui/web/src/i18n/locales/zh/agents.json b/ui/web/src/i18n/locales/zh/agents.json
index 63d947e9..a6b8d04f 100644
--- a/ui/web/src/i18n/locales/zh/agents.json
+++ b/ui/web/src/i18n/locales/zh/agents.json
@@ -533,7 +533,12 @@
"retrying": "重试中...",
"done": "完成",
"fileLabelSOUL": "灵魂与个性",
- "fileLabelIDENTITY": "身份卡"
+ "fileLabelIDENTITY": "身份卡",
+ "regenerateTitle": "AI 编辑中...",
+ "regenerateCompleted": "编辑完成!",
+ "regenerateFailed": "编辑失败",
+ "regenerateWeavingSoul": "正在更新文件:",
+ "regenerateWait": "AI 正在重写文件,请稍候..."
},
"configGroups": {
"core": "核心",
diff --git a/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx b/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx
index e7dd6360..6a6e22a0 100644
--- a/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx
+++ b/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx
@@ -30,6 +30,7 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
const { deleteAgent: deleteAgentById } = useAgents();
const hb = useAgentHeartbeat(agentId);
const [summoningOpen, setSummoningOpen] = useState(false);
+ const [summoningMode, setSummoningMode] = useState<"summon" | "regenerate">("summon");
const [activeTab, setActiveTab] = useState("agent");
const [deleteOpen, setDeleteOpen] = useState(false);
const [advancedOpen, setAdvancedOpen] = useState(false);
@@ -37,11 +38,13 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
const handleRegenerate = async (prompt: string) => {
await regenerateAgent(prompt);
+ setSummoningMode("regenerate");
setSummoningOpen(true);
};
const handleResummon = async () => {
await resummonAgent();
+ setSummoningMode("summon");
setSummoningOpen(true);
};
@@ -130,6 +133,7 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
agentName={title}
onCompleted={() => {}}
onResummon={async () => { await resummonAgent(); }}
+ mode={summoningMode}
/>
{heartbeatOpen && (
diff --git a/ui/web/src/pages/agents/summoning-modal.tsx b/ui/web/src/pages/agents/summoning-modal.tsx
index a76883a3..e0d40d8d 100644
--- a/ui/web/src/pages/agents/summoning-modal.tsx
+++ b/ui/web/src/pages/agents/summoning-modal.tsx
@@ -19,6 +19,8 @@ interface SummoningModalProps {
onResummon: (agentId: string) => Promise;
hideClose?: boolean;
onContinue?: () => void;
+ /** "summon" (default) shows summoning language; "regenerate" shows edit-with-AI language */
+ mode?: "summon" | "regenerate";
}
const SUMMONING_FILES = [
@@ -35,7 +37,9 @@ export function SummoningModal({
onResummon,
hideClose = false,
onContinue,
+ mode = "summon",
}: SummoningModalProps) {
+ const isRegenerate = mode === "regenerate";
const { t } = useTranslation("agents");
const [generatedFiles, setGeneratedFiles] = useState([]);
const [status, setStatus] = useState<"summoning" | "completed" | "failed">("summoning");
@@ -120,10 +124,10 @@ export function SummoningModal({
{status === "completed"
- ? t("summoning.completed")
+ ? t(isRegenerate ? "summoning.regenerateCompleted" : "summoning.completed")
: status === "failed"
- ? t("summoning.failed")
- : t("summoning.title")}
+ ? t(isRegenerate ? "summoning.regenerateFailed" : "summoning.failed")
+ : t(isRegenerate ? "summoning.regenerateTitle" : "summoning.title")}
@@ -165,7 +169,7 @@ export function SummoningModal({
: { duration: 0.5 }
}
>
- {status === "completed" ? "\u2728" : status === "failed" ? "\u{1F4A8}" : "\u{1FA84}"}
+ {status === "completed" ? "\u2728" : status === "failed" ? "\u{1F4A8}" : isRegenerate ? "\u270F\uFE0F" : "\u{1FA84}"}
@@ -180,7 +184,7 @@ export function SummoningModal({
{errorMsg || t("summoning.failed")}
) : (
- <>{t("summoning.weavingSoul")} {agentName}...>
+ <>{t(isRegenerate ? "summoning.regenerateWeavingSoul" : "summoning.weavingSoul")} {agentName}...>
)}
@@ -231,7 +235,7 @@ export function SummoningModal({
{status === "summoning" && (
- {t("summoning.wait")} ({Math.floor(elapsed / 60)}:{String(elapsed % 60).padStart(2, "0")})
+ {t(isRegenerate ? "summoning.regenerateWait" : "summoning.wait")} ({Math.floor(elapsed / 60)}:{String(elapsed % 60).padStart(2, "0")})
)}
diff --git a/ui/web/src/pages/mcp/hooks/use-mcp.ts b/ui/web/src/pages/mcp/hooks/use-mcp.ts
index 3af2e3f3..8e5247fe 100644
--- a/ui/web/src/pages/mcp/hooks/use-mcp.ts
+++ b/ui/web/src/pages/mcp/hooks/use-mcp.ts
@@ -12,7 +12,7 @@ export function useMCP() {
const http = useHttp();
const queryClient = useQueryClient();
- const { data: servers = [], isLoading: loading } = useQuery({
+ const { data: servers = [], isLoading: loading, isFetching: fetching } = useQuery({
queryKey: queryKeys.mcp.all,
queryFn: async () => {
const res = await http.get<{ servers: MCPServerData[] }>("/v1/mcp/servers");
@@ -157,6 +157,7 @@ export function useMCP() {
return {
servers,
loading,
+ fetching,
refresh: invalidate,
createServer,
updateServer,
diff --git a/ui/web/src/pages/mcp/mcp-page.tsx b/ui/web/src/pages/mcp/mcp-page.tsx
index f13878e1..86f1ef23 100644
--- a/ui/web/src/pages/mcp/mcp-page.tsx
+++ b/ui/web/src/pages/mcp/mcp-page.tsx
@@ -27,8 +27,8 @@ const transportBadge: Record = {
export function MCPPage() {
const { t } = useTranslation("mcp");
const { t: tc } = useTranslation("common");
- const { servers, loading, refresh, createServer, updateServer, deleteServer, grantAgent, revokeAgent, listAgentGrants, testConnection, reconnectServer, listServerTools, getUserCredentials, setUserCredentials, deleteUserCredentials } = useMCP();
- const spinning = useMinLoading(loading);
+ const { servers, loading, fetching, refresh, createServer, updateServer, deleteServer, grantAgent, revokeAgent, listAgentGrants, testConnection, reconnectServer, listServerTools, getUserCredentials, setUserCredentials, deleteUserCredentials } = useMCP();
+ const spinning = useMinLoading(fetching);
const showSkeleton = useDeferredLoading(loading && servers.length === 0);
const [search, setSearch] = useState("");
const [formOpen, setFormOpen] = useState(false);
diff --git a/ui/web/src/pages/memory/kg-entity-detail-dialog.tsx b/ui/web/src/pages/memory/kg-entity-detail-dialog.tsx
index 9881c1d5..325928b5 100644
--- a/ui/web/src/pages/memory/kg-entity-detail-dialog.tsx
+++ b/ui/web/src/pages/memory/kg-entity-detail-dialog.tsx
@@ -84,7 +84,7 @@ export function KGEntityDetailDialog({ open, onOpenChange, agentId, entity, getE
return (