diff --git a/.env.development b/.env.development
index e9bd8c3..74f1a42 100644
--- a/.env.development
+++ b/.env.development
@@ -1,10 +1,10 @@
-# NEXT_PUBLIC_JAVA_URL = https://77.app.java.auth.qikongjian.com
-# NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com
+NEXT_PUBLIC_JAVA_URL = https://77.app.java.auth.qikongjian.com
+NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com
# NEXT_PUBLIC_CUT_URL = https://smartcut.movieflow.ai
-NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai
-NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai
+# NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai
+# NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai
NEXT_PUBLIC_CUT_URL = https://smartcut.movieflow.ai
# 失败率
NEXT_PUBLIC_ERROR_CONFIG = 0.1
\ No newline at end of file
diff --git a/api/DTO/movieEdit.ts b/api/DTO/movieEdit.ts
index c7681f2..9a457f4 100644
--- a/api/DTO/movieEdit.ts
+++ b/api/DTO/movieEdit.ts
@@ -675,7 +675,7 @@ export const LOADING_TEXT_MAP = {
} as const;
export type Status = 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';
-export type Stage = 'script' | 'character' | 'scene' | 'shot_sketch' | 'video' | 'final_video';
+export type Stage = 'init' | 'script' | 'character' | 'scene' | 'shot_sketch' | 'video' | 'final_video';
// 添加 TaskObject 接口
export interface TaskObject {
title: string; // 标题
diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx
index 793f632..29191b6 100644
--- a/components/pages/work-flow.tsx
+++ b/components/pages/work-flow.tsx
@@ -279,34 +279,30 @@ const WorkFlow = React.memo(function WorkFlow() {
className="videoContainer-qteKNi"
ref={containerRef}
>
- {isLoading ? (
-
- ) : (
-
- setIsSmartChatBoxOpen(true)}
- setVideoPreview={(url, id) => {
- setPreviewVideoUrl(url);
- setPreviewVideoId(id);
- }}
- showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'}
- onGotoCut={generateEditPlan}
- isSmartChatBoxOpen={isSmartChatBoxOpen}
- onRetryVideo={(video_id) => handleRetryVideo(video_id)}
- />
-
- )}
+
+ setIsSmartChatBoxOpen(true)}
+ setVideoPreview={(url, id) => {
+ setPreviewVideoUrl(url);
+ setPreviewVideoId(id);
+ }}
+ showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'}
+ onGotoCut={generateEditPlan}
+ isSmartChatBoxOpen={isSmartChatBoxOpen}
+ onRetryVideo={(video_id) => handleRetryVideo(video_id)}
+ />
+
{taskObject.currentStage !== 'script' && (
diff --git a/components/pages/work-flow/media-viewer.tsx b/components/pages/work-flow/media-viewer.tsx
index 6aa656f..ddda0f6 100644
--- a/components/pages/work-flow/media-viewer.tsx
+++ b/components/pages/work-flow/media-viewer.tsx
@@ -8,6 +8,7 @@ import { GlassIconButton } from '@/components/ui/glass-icon-button';
import { ScriptRenderer } from '@/components/script-renderer/ScriptRenderer';
import { mockScriptData } from '@/components/script-renderer/mock';
import { Skeleton } from '@/components/ui/skeleton';
+import ScriptLoading from './script-loading';
import { TaskObject } from '@/api/DTO/movieEdit';
import { Button, Tooltip } from 'antd';
import { downloadVideo, downloadAllVideos, getFirstFrame } from '@/utils/tools';
@@ -675,30 +676,19 @@ export const MediaViewer = React.memo(function MediaViewer({
// 渲染剧本
const renderScriptContent = () => {
return (
-
- {
- scriptData ? (
-
- ) : (
-
- )
- }
+
+ {scriptData ? (
+
+ ) : (
+
+ )}
);
};
diff --git a/components/pages/work-flow/script-loading.tsx b/components/pages/work-flow/script-loading.tsx
new file mode 100644
index 0000000..0997c31
--- /dev/null
+++ b/components/pages/work-flow/script-loading.tsx
@@ -0,0 +1,126 @@
+'use client';
+
+import React, { useEffect, useMemo, useRef, useState } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { Loader2 } from 'lucide-react';
+
+interface ScriptLoadingProps {
+ /** When true, progress snaps to 100% */
+ isCompleted?: boolean;
+ /** Estimated total duration in ms to reach ~95% (default 80s) */
+ estimatedMs?: number;
+}
+
+/**
+ * Dark-themed loading with spinner, staged copy and progress bar.
+ * Progress linearly approaches 95% over estimatedMs, then snaps to 100% if isCompleted=true.
+ */
+export const ScriptLoading: React.FC
= ({ isCompleted = false, estimatedMs = 80000 }) => {
+ const [progress, setProgress] = useState(0);
+ const intervalRef = useRef(null);
+
+ const targetWhenPending = 95; // cap before data arrives
+ const tickMs = 200; // update cadence
+
+ // staged messages by progress
+ const stageMessage = useMemo(() => {
+ if (progress >= 100) return 'Done';
+ if (progress >= 95) return 'Almost done, please wait...';
+ if (progress >= 82) return 'Generating script and prompt...';
+ if (progress >= 63) return 'Polishing dialogue and transitions...';
+ if (progress >= 45) return 'Arranging shots and rhythm...';
+ if (progress >= 28) return 'Shaping characters and scene details...';
+ if (progress >= 12) return 'Outlining the story...';
+ return 'Waking up the creative engine...';
+ }, [progress]);
+
+ // progress auto-increment
+ useEffect(() => {
+ const desiredTarget = isCompleted ? 100 : targetWhenPending;
+
+ // when completed, quickly animate to 100
+ if (isCompleted) {
+ setProgress((prev) => (prev < 100 ? Math.max(prev, 96) : 100));
+ }
+
+ if (intervalRef.current) clearInterval(intervalRef.current);
+
+ // compute linear increment to reach target in remaining estimated time
+ const totalTicks = Math.ceil(estimatedMs / tickMs);
+ const baseIncrement = (targetWhenPending - 0) / Math.max(totalTicks, 1);
+
+ intervalRef.current = setInterval(() => {
+ setProgress((prev) => {
+ const target = isCompleted ? 100 : desiredTarget;
+ if (prev >= target) return prev;
+ const remaining = target - prev;
+ const step = Math.max(Math.min(baseIncrement, remaining * 0.25), 0.2);
+ const next = Math.min(prev + step, target);
+ return Number(next.toFixed(2));
+ });
+ }, tickMs);
+
+ return () => {
+ if (intervalRef.current) clearInterval(intervalRef.current);
+ };
+ }, [isCompleted, estimatedMs]);
+
+ const widthStyle = { width: `${Math.min(progress, 100)}%` };
+
+ return (
+
+
+ {/* subtle animated halo */}
+ {/*
*/}
+
+
+
+ {stageMessage}
+
+
+
+
+
+
+
+
+ Generating Script...
+ {Math.round(progress)}%
+
+
+
+
+ );
+};
+
+export default ScriptLoading;
+
+
diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx
index d743dd4..95c282c 100644
--- a/components/pages/work-flow/use-workflow-data.tsx
+++ b/components/pages/work-flow/use-workflow-data.tsx
@@ -44,7 +44,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
let tempTaskObject = useRef({
title: '',
tags: [],
- currentStage: 'script' as Stage,
+ currentStage: 'init' as Stage,
status: 'IN_PROGRESS' as Status,
roles: {
data: [],
@@ -550,6 +550,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
taskCurrent.title = name || 'generating...';
taskCurrent.tags = tags || [];
taskCurrent.status = status as Status;
+ taskCurrent.currentStage = 'script';
// 设置标题
if (!name) {