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
This commit is contained in:
viettranx committed 2026-03-31 23:07:15 +07:00
1 parent ef0de0d760
commit aaa56ff004
9 files changed
+38 -14

No files matched your search

+6 -1
View File
@@ -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",
+6 -1
View File
@@ -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",
+6 -1
View File
@@ -533,7 +533,12 @@
"retrying": "重试中...",
"done": "完成",
"fileLabelSOUL": "灵魂与个性",
"fileLabelIDENTITY": "身份卡"
"fileLabelIDENTITY": "身份卡",
"regenerateTitle": "AI 编辑中...",
"regenerateCompleted": "编辑完成!",
"regenerateFailed": "编辑失败",
"regenerateWeavingSoul": "正在更新文件:",
"regenerateWait": "AI 正在重写文件,请稍候..."
},
"configGroups": {
"core": "核心",
@@ -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 && (
+10 -6
View File
@@ -19,6 +19,8 @@ interface SummoningModalProps {
onResummon: (agentId: string) => Promise<void>;
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<string[]>([]);
const [status, setStatus] = useState<"summoning" | "completed" | "failed">("summoning");
@@ -120,10 +124,10 @@ export function SummoningModal({
<DialogHeader>
<DialogTitle className="text-center">
{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")}
</DialogTitle>
</DialogHeader>
@@ -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}"}
</motion.div>
</div>
@@ -180,7 +184,7 @@ export function SummoningModal({
{errorMsg || t("summoning.failed")}
</span>
) : (
<>{t("summoning.weavingSoul")} <span className="font-semibold text-foreground">{agentName}</span>...</>
<>{t(isRegenerate ? "summoning.regenerateWeavingSoul" : "summoning.weavingSoul")} <span className="font-semibold text-foreground">{agentName}</span>...</>
)}
</p>
@@ -231,7 +235,7 @@ export function SummoningModal({
{status === "summoning" && (
<p className="text-center text-xs text-muted-foreground tabular-nums">
{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")})
</p>
)}
+2 -1
View File
@@ -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,
+2 -2
View File
@@ -27,8 +27,8 @@ const transportBadge: Record<string, string> = {
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);
@@ -84,7 +84,7 @@ export function KGEntityDetailDialog({ open, onOpenChange, agentId, entity, getE
return (
<Dialog open={open} onOpenChange={(v) => !traversing && onOpenChange(v)}>
<DialogContent aria-describedby={undefined} className="max-w-7xl max-h-[90vh] flex flex-col">
<DialogContent aria-describedby={undefined} className="max-h-[90vh] w-[95vw] flex flex-col sm:max-w-5xl">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<span>{entity?.name}</span>
@@ -108,7 +108,7 @@ export function MemoryDocumentDialog({ open, onOpenChange, agentId, document }:
return (
<Dialog open={open} onOpenChange={(v) => !saving && onOpenChange(v)}>
<DialogContent aria-describedby={undefined} className="max-w-4xl max-h-[85vh] flex flex-col">
<DialogContent aria-describedby={undefined} className="max-h-[85vh] w-[95vw] flex flex-col sm:max-w-4xl">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<span className="font-mono text-sm">{document?.path}</span>