diff --git a/src/app/[locale]/studio/render/[projectId]/page.tsx b/src/app/[locale]/studio/render/[projectId]/page.tsx index bdac867..3dc380e 100644 --- a/src/app/[locale]/studio/render/[projectId]/page.tsx +++ b/src/app/[locale]/studio/render/[projectId]/page.tsx @@ -10,6 +10,7 @@ import { Link2, Loader2, RefreshCw, + XCircle, } from "lucide-react"; import { useLocale } from "next-intl"; @@ -205,6 +206,12 @@ export default function RenderPage() { } else if (data.status === "failed") { setPhase("failed"); setErrorMessage(data.errorMessage ?? "Render failed."); + } else if (data.status?.toLowerCase() === "cancelled") { + // Cancelled here or from another surface (pill / dashboard) — reflect it. + setPhase("config"); + setProgress(0); + setEtaSec(null); + setErrorMessage("رندر لغو شد."); } } catch { setPhase("failed"); @@ -279,6 +286,26 @@ export default function RenderPage() { } }, [projectId, resolution, fps, locale]); + /** Fully cancel the active render — works in any state (Queued or Running). The + * server marks it Cancelled (the dev worker abandons it, a real node kills its + * process), so it stops fully. Optimistically return to config right away. */ + const cancelRender = useCallback(async () => { + if (!jobId) return; + const id = jobId; + setPhase("config"); + setProgress(0); + setEtaSec(null); + setProgressMessage(""); + setPreviewB64(null); + setErrorMessage("رندر لغو شد."); + setJobId(null); + try { + await apiFetch(`/api/renders/${id}/cancel`, { method: "POST" }); + } catch { + /* server is the source of truth; the dashboard can also cancel */ + } + }, [jobId]); + const backToStudio = `/studio/video/${projectId}`; const isBusy = phase === "submitting" || phase === "polling"; @@ -393,9 +420,19 @@ export default function RenderPage() { ) : isBusy ? ( -

- می‌توانید این صفحه را ببندید؛ رندر در پس‌زمینه ادامه می‌یابد و از هر صفحه‌ای قابل پیگیری است. -

+
+

+ می‌توانید این صفحه را ببندید؛ رندر در پس‌زمینه ادامه می‌یابد و از هر صفحه‌ای قابل پیگیری است. +

+ +
) : ( // Config
diff --git a/src/components/render/GlobalRenderProgress.tsx b/src/components/render/GlobalRenderProgress.tsx index f831c25..e6ad8b8 100644 --- a/src/components/render/GlobalRenderProgress.tsx +++ b/src/components/render/GlobalRenderProgress.tsx @@ -103,16 +103,25 @@ export function GlobalRenderProgress({ authed }: { authed: boolean }) {
- {/* Dismiss (hides until job id changes) */} + {/* Cancel — fully stops the render server-side (works from any page). */} { + onClick={async (e) => { e.stopPropagation(); - setDismissed(active.id); + if (!window.confirm("این رندر لغو شود؟")) return; + const id = active.id; + setActive(null); + setDismissed(id); + try { + await fetch(`/api/renders/${id}/cancel`, { method: "POST" }); + } catch { + /* server is the source of truth; ignore transient errors */ + } }} - className="shrink-0 rounded p-1 text-gray-600 hover:text-gray-300" - aria-label="پنهان کردن" + className="shrink-0 rounded p-1 text-gray-500 transition-colors hover:bg-red-500/20 hover:text-red-300" + aria-label="لغو رندر" + title="لغو رندر" >