mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 10:18:42 +00:00
feat(web): add cancel-summon button in SummoningModal
Add Cancel button in SummoningModal after 60s threshold. Call POST
/v1/agents/{id}/cancel-summon via useHttp hook. Update agent status
to summon_failed. Add i18n strings for en/vi/zh in agents locale.
This commit is contained in:
1 parent
d7342fbd6e
commit
2a7a8f9a84
8 files changed
+53
-7
No files matched your search
@@ -724,7 +724,10 @@
|
||||
"retrying": "Retrying...",
|
||||
"done": "done",
|
||||
"fileLabelSOUL": "Soul & Personality",
|
||||
"fileLabelIDENTITY": "Identity Card"
|
||||
"fileLabelIDENTITY": "Identity Card",
|
||||
"takingTooLong": "Taking too long?",
|
||||
"cancel": "Cancel",
|
||||
"cancelling": "Cancelling..."
|
||||
},
|
||||
"configGroups": {
|
||||
"core": "Core",
|
||||
|
||||
@@ -709,7 +709,10 @@
|
||||
"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",
|
||||
"takingTooLong": "Mất quá nhiều thời gian?",
|
||||
"cancel": "Huỷ",
|
||||
"cancelling": "Đang huỷ..."
|
||||
},
|
||||
"configGroups": {
|
||||
"core": "Cốt lõi",
|
||||
|
||||
@@ -709,7 +709,10 @@
|
||||
"retrying": "重试中...",
|
||||
"done": "完成",
|
||||
"fileLabelSOUL": "灵魂与个性",
|
||||
"fileLabelIDENTITY": "身份卡"
|
||||
"fileLabelIDENTITY": "身份卡",
|
||||
"takingTooLong": "耗时过长?",
|
||||
"cancel": "取消",
|
||||
"cancelling": "正在取消..."
|
||||
},
|
||||
"configGroups": {
|
||||
"core": "核心",
|
||||
|
||||
@@ -35,7 +35,7 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
|
||||
const navigate = useNavigate();
|
||||
const { agent, files, loading, updateAgent, getFile, setFile, regenerateAgent, resummonAgent, refresh } =
|
||||
useAgentDetail(agentId);
|
||||
const { deleteAgent: deleteAgentById } = useAgents();
|
||||
const { deleteAgent: deleteAgentById, cancelSummonAgent } = useAgents();
|
||||
const hb = useAgentHeartbeat(agentId);
|
||||
const [summoningOpen, setSummoningOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("agent");
|
||||
@@ -161,6 +161,7 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
|
||||
agentName={title}
|
||||
onCompleted={() => {}}
|
||||
onResummon={async () => { await resummonAgent(); }}
|
||||
onCancel={cancelSummonAgent}
|
||||
/>
|
||||
|
||||
{heartbeatOpen && (
|
||||
|
||||
@@ -31,7 +31,7 @@ export function AgentsPage() {
|
||||
const { t } = useTranslation("agents");
|
||||
const { id: detailId } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { agents, loading, createAgent, deleteAgent, refresh, resummonAgent } = useAgents();
|
||||
const { agents, loading, createAgent, deleteAgent, refresh, resummonAgent, cancelSummonAgent } = useAgents();
|
||||
const showSkeleton = useDeferredLoading(loading && agents.length === 0);
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -288,6 +288,7 @@ export function AgentsPage() {
|
||||
agentName={summoningAgent.name}
|
||||
onCompleted={refresh}
|
||||
onResummon={resummonAgent}
|
||||
onCancel={cancelSummonAgent}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -113,5 +113,12 @@ export function useAgents() {
|
||||
[http],
|
||||
);
|
||||
|
||||
return { agents, loading, error, refresh: invalidate, createAgent, updateAgent, deleteAgent, resummonAgent };
|
||||
const cancelSummonAgent = useCallback(
|
||||
async (id: string) => {
|
||||
await http.post(`/v1/agents/${id}/cancel-summon`);
|
||||
},
|
||||
[http],
|
||||
);
|
||||
|
||||
return { agents, loading, error, refresh: invalidate, createAgent, updateAgent, deleteAgent, resummonAgent, cancelSummonAgent };
|
||||
}
|
||||
@@ -17,10 +17,13 @@ interface SummoningModalProps {
|
||||
agentName: string;
|
||||
onCompleted: () => void;
|
||||
onResummon: (agentId: string) => Promise<void>;
|
||||
onCancel?: (agentId: string) => Promise<void>;
|
||||
hideClose?: boolean;
|
||||
onContinue?: () => void;
|
||||
}
|
||||
|
||||
const CANCEL_THRESHOLD_SEC = 60;
|
||||
|
||||
const SUMMONING_FILES = [
|
||||
{ name: "SOUL.md", required: true },
|
||||
{ name: "IDENTITY.md", required: true },
|
||||
@@ -33,6 +36,7 @@ export function SummoningModal({
|
||||
agentName,
|
||||
onCompleted,
|
||||
onResummon,
|
||||
onCancel,
|
||||
hideClose = false,
|
||||
onContinue,
|
||||
}: SummoningModalProps) {
|
||||
@@ -41,6 +45,7 @@ export function SummoningModal({
|
||||
const [status, setStatus] = useState<"summoning" | "completed" | "failed">("summoning");
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
const [retrying, setRetrying] = useState(false);
|
||||
const [cancelling, setCancelling] = useState(false);
|
||||
const [elapsed, setElapsed] = useState(0);
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
@@ -100,6 +105,19 @@ export function SummoningModal({
|
||||
|
||||
useWsEvent("agent.summoning", handleSummoningEvent);
|
||||
|
||||
const handleCancel = async () => {
|
||||
if (!onCancel) return;
|
||||
setCancelling(true);
|
||||
try {
|
||||
await onCancel(agentId);
|
||||
// BE emits WS type=failed → existing handler closes modal
|
||||
} catch {
|
||||
// keep modal open
|
||||
} finally {
|
||||
setCancelling(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRetry = async () => {
|
||||
setRetrying(true);
|
||||
try {
|
||||
@@ -235,6 +253,15 @@ export function SummoningModal({
|
||||
</p>
|
||||
)}
|
||||
|
||||
{status === "summoning" && elapsed >= CANCEL_THRESHOLD_SEC && onCancel && (
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<p className="text-xs text-muted-foreground">{t("summoning.takingTooLong")}</p>
|
||||
<Button variant="ghost" size="sm" onClick={handleCancel} disabled={cancelling}>
|
||||
{cancelling ? t("summoning.cancelling") : t("summoning.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "completed" && onContinue && (
|
||||
<Button size="sm" onClick={onContinue}>
|
||||
{t("summoning.continue")}
|
||||
|
||||
@@ -26,7 +26,7 @@ interface StepAgentProps {
|
||||
|
||||
export function StepAgent({ provider, model, onComplete, onBack, existingAgent }: StepAgentProps) {
|
||||
const { t } = useTranslation("setup");
|
||||
const { createAgent, updateAgent, deleteAgent, resummonAgent } = useAgents();
|
||||
const { createAgent, updateAgent, deleteAgent, resummonAgent, cancelSummonAgent } = useAgents();
|
||||
const agentPresets = useAgentPresets();
|
||||
|
||||
const isEditing = !!existingAgent;
|
||||
@@ -274,6 +274,7 @@ export function StepAgent({ provider, model, onComplete, onBack, existingAgent }
|
||||
agentName={createdAgent.name}
|
||||
onCompleted={handleSummoningComplete}
|
||||
onResummon={resummonAgent}
|
||||
onCancel={cancelSummonAgent}
|
||||
hideClose
|
||||
onContinue={summoningOutcome === "success" ? handleContinue : undefined}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user