From eed86c43d7006040abf85fb002ab19d7bc4c7d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sat, 11 Oct 2025 11:45:30 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=B0=86=E4=B8=8B=E8=BD=BD=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=20=E6=8A=BD=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/work-flow/H5MediaViewer.tsx | 139 +---------------- .../work-flow/download-options-modal.tsx | 147 ++++++++++++++++++ components/pages/work-flow/media-viewer.tsx | 91 +++++++---- 3 files changed, 208 insertions(+), 169 deletions(-) create mode 100644 components/pages/work-flow/download-options-modal.tsx diff --git a/components/pages/work-flow/H5MediaViewer.tsx b/components/pages/work-flow/H5MediaViewer.tsx index ab65804..804beef 100644 --- a/components/pages/work-flow/H5MediaViewer.tsx +++ b/components/pages/work-flow/H5MediaViewer.tsx @@ -11,7 +11,7 @@ import { GlassIconButton } from '@/components/ui/glass-icon-button'; import { downloadVideo, downloadAllVideos, getFirstFrame } from '@/utils/tools'; import { Drawer } from 'antd'; import error_image from '@/public/assets/error.webp'; -import { createRoot, Root } from 'react-dom/client'; +import { showDownloadOptionsModal } from './download-options-modal'; interface H5MediaViewerProps { /** 任务对象,包含各阶段数据 */ @@ -51,143 +51,6 @@ interface H5MediaViewerProps { aspectRatio?: string; } -interface DownloadOptionsModalProps { - onDownloadCurrent: () => void; - onDownloadAll: () => void; - onClose: () => void; - currentVideoIndex: number; - totalVideos: number; - /** 当前视频是否生成失败 */ - isCurrentVideoFailed: boolean; - /** 是否为最终视频阶段 */ - isFinalStage?: boolean; -} - -function DownloadOptionsModal(props: DownloadOptionsModalProps) { - const { onDownloadCurrent, onDownloadAll, onClose, currentVideoIndex, totalVideos, isCurrentVideoFailed, isFinalStage = false } = props; - const containerRef = useRef(null); - - useEffect(() => { - const originalOverflow = document.body.style.overflow; - document.body.style.overflow = 'hidden'; - return () => { - document.body.style.overflow = originalOverflow; - }; - }, []); - - return ( -
-
e.stopPropagation()} - > - -
-
- -
-

- Download Options -

-
- -
-

- Choose your download preference -

- - {!isCurrentVideoFailed && ( -
-
Current video
-
{currentVideoIndex + 1} / {totalVideos}
-
- )} -
- -
- {!isCurrentVideoFailed && ( - - )} - -
-
-
- ); -} - -/** - * Opens a download options modal with glass morphism style. - * @param {DownloadOptionsModalProps} options - download options and callbacks. - */ -function showDownloadOptionsModal(options: Omit): void { - if (typeof window === 'undefined' || typeof document === 'undefined') { - return; - } - - const mount = document.createElement('div'); - mount.setAttribute('data-alt', 'download-options-modal-root'); - document.body.appendChild(mount); - - let root: Root | null = null; - try { - root = createRoot(mount); - } catch { - if (mount.parentNode) { - mount.parentNode.removeChild(mount); - } - return; - } - - const close = () => { - try { - root?.unmount(); - } finally { - if (mount.parentNode) { - mount.parentNode.removeChild(mount); - } - } - }; - - root.render( - - ); -} - /** * 面向 H5 的媒体预览组件。 * - 除剧本阶段外,统一使用 antd Carousel 展示 图片/视频。 diff --git a/components/pages/work-flow/download-options-modal.tsx b/components/pages/work-flow/download-options-modal.tsx new file mode 100644 index 0000000..1904059 --- /dev/null +++ b/components/pages/work-flow/download-options-modal.tsx @@ -0,0 +1,147 @@ +'use client'; + +import React, { useEffect, useRef } from 'react'; +import { createRoot, Root } from 'react-dom/client'; +import { X, Download, ArrowDownWideNarrow } from 'lucide-react'; + +interface DownloadOptionsModalProps { + onDownloadCurrent: () => void; + onDownloadAll: () => void; + onClose: () => void; + currentVideoIndex: number; + totalVideos: number; + /** 当前视频是否生成失败 */ + isCurrentVideoFailed: boolean; + /** 是否为最终视频阶段 */ + isFinalStage?: boolean; +} + +/** + * Download options modal component with glass morphism style. + * @param {DownloadOptionsModalProps} props - modal properties. + */ +function DownloadOptionsModal(props: DownloadOptionsModalProps) { + const { onDownloadCurrent, onDownloadAll, onClose, currentVideoIndex, totalVideos, isCurrentVideoFailed, isFinalStage = false } = props; + const containerRef = useRef(null); + + useEffect(() => { + const originalOverflow = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = originalOverflow; + }; + }, []); + + return ( +
+
e.stopPropagation()} + > + +
+
+ +
+

+ Download Options +

+
+ +
+

+ Choose your download preference +

+ + {!isCurrentVideoFailed && ( +
+
Current video
+
{currentVideoIndex + 1} / {totalVideos}
+
+ )} +
+ +
+ {!isCurrentVideoFailed && ( + + )} + +
+
+
+ ); +} + +/** + * Opens a download options modal with glass morphism style. + * @param {DownloadOptionsModalProps} options - download options and callbacks. + */ +export function showDownloadOptionsModal(options: Omit): void { + if (typeof window === 'undefined' || typeof document === 'undefined') { + return; + } + + const mount = document.createElement('div'); + mount.setAttribute('data-alt', 'download-options-modal-root'); + document.body.appendChild(mount); + + let root: Root | null = null; + try { + root = createRoot(mount); + } catch { + if (mount.parentNode) { + mount.parentNode.removeChild(mount); + } + return; + } + + const close = () => { + try { + root?.unmount(); + } finally { + if (mount.parentNode) { + mount.parentNode.removeChild(mount); + } + } + }; + + root.render( + + ); +} + diff --git a/components/pages/work-flow/media-viewer.tsx b/components/pages/work-flow/media-viewer.tsx index fb38313..a65d7e2 100644 --- a/components/pages/work-flow/media-viewer.tsx +++ b/components/pages/work-flow/media-viewer.tsx @@ -3,6 +3,7 @@ import React, { useRef, useEffect, useState, SetStateAction, useMemo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Edit3, Play, Pause, Volume2, VolumeX, Maximize, Minimize, Loader2, X, Scissors, RotateCcw, MessageCircleMore, Download, ArrowDownWideNarrow, PictureInPicture2, PenTool } from 'lucide-react'; +import { showDownloadOptionsModal } from './download-options-modal'; import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal'; import { GlassIconButton } from '@/components/ui/glass-icon-button'; import { ScriptRenderer } from '@/components/script-renderer/ScriptRenderer'; @@ -505,21 +506,33 @@ export const MediaViewer = React.memo(function MediaViewer({ onClick={() => handleEditClick('3', 'final')} /> - {/* 下载所有视频按钮 */} - - { - setIsLoadingDownloadAllVideosBtn(true); - await downloadAllVideos(taskObject.videos.data.flatMap((video: any) => video.urls)); - setIsLoadingDownloadAllVideosBtn(false); - }} /> - {/* 下载按钮 */} - - { - setIsLoadingDownloadBtn(true); - await downloadVideo(taskObject.final.url); - setIsLoadingDownloadBtn(false); - }} /> + + { + const totalVideos = taskObject.videos.data.filter((video: any) => video.urls && video.urls.length > 0).length; + showDownloadOptionsModal({ + currentVideoIndex: 0, + totalVideos: totalVideos + 1, + isCurrentVideoFailed: false, + isFinalStage: true, + onDownloadCurrent: async () => { + setIsLoadingDownloadBtn(true); + await downloadVideo(taskObject.final.url); + setIsLoadingDownloadBtn(false); + }, + onDownloadAll: async () => { + setIsLoadingDownloadAllVideosBtn(true); + const all = taskObject.videos.data.flatMap((video: any) => video.urls); + all.push(taskObject.final.url); + await downloadAllVideos(all); + setIsLoadingDownloadAllVideosBtn(false); + } + }); + }} + /> {showGotoCutButton && ( @@ -648,15 +661,39 @@ export const MediaViewer = React.memo(function MediaViewer({ )} - - { - const currentVideo = taskObject.videos.data[currentSketchIndex]; - if (currentVideo && currentVideo.urls && currentVideo.urls.length > 0) { - setIsLoadingDownloadBtn(true); - await downloadVideo(currentVideo.urls[0]); - setIsLoadingDownloadBtn(false); - } - }} /> + + { + const currentVideo = taskObject.videos.data[currentSketchIndex]; + const totalVideos = taskObject.videos.data.filter((video: any) => video.urls && video.urls.length > 0).length; + const isCurrentVideoFailed = currentVideo.video_status === 2; + + showDownloadOptionsModal({ + currentVideoIndex: currentSketchIndex, + totalVideos: taskObject.final.url ? totalVideos + 1 : totalVideos, + isCurrentVideoFailed: isCurrentVideoFailed, + isFinalStage: false, + onDownloadCurrent: async () => { + if (currentVideo && currentVideo.urls && currentVideo.urls.length > 0) { + setIsLoadingDownloadBtn(true); + await downloadVideo(currentVideo.urls[0]); + setIsLoadingDownloadBtn(false); + } + }, + onDownloadAll: async () => { + setIsLoadingDownloadAllVideosBtn(true); + const all = taskObject.videos.data.flatMap((video: any) => video.urls); + if (taskObject.final.url) { + all.push(taskObject.final.url); + } + await downloadAllVideos(all); + setIsLoadingDownloadAllVideosBtn(false); + } + }); + }} + /> ) : ( @@ -681,14 +718,6 @@ export const MediaViewer = React.memo(function MediaViewer({ } }} /> - {/* 下载所有视频按钮 */} - - { - setIsLoadingDownloadAllVideosBtn(true); - await downloadAllVideos(taskObject.videos.data.flatMap((video: any) => video.urls)); - setIsLoadingDownloadAllVideosBtn(false); - }} /> - {/* 跳转剪辑按钮 */} {showGotoCutButton && ( From c5ec5d5e7ac4028a7a346a8015de4c513cd42989 Mon Sep 17 00:00:00 2001 From: moux1024 <403053463@qq.com> Date: Sat, 11 Oct 2025 16:19:45 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20credit=E8=A7=84?= =?UTF-8?q?=E5=88=99&=E9=9A=90=E8=97=8F=E7=AD=BE=E5=88=B0=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/share/page.tsx | 34 ++++++++++++---------------------- components/layout/top-bar.tsx | 4 ++-- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/app/share/page.tsx b/app/share/page.tsx index f81c356..3751583 100644 --- a/app/share/page.tsx +++ b/app/share/page.tsx @@ -267,7 +267,7 @@ export default function SharePage(): JSX.Element { Step 3 Reward -

You both receive rewards after your friend activates their account.

+

You will receive 500 credits after your friends successfully sign in.

@@ -275,33 +275,23 @@ export default function SharePage(): JSX.Element {

MovieFlow Credits Rewards Program

-

Welcome to MovieFlow! Our Credits Program is designed to reward your growth and contributions. Credits can be redeemed for premium templates, effects, and membership time.

+

+ Welcome to MovieFlow! Our Credits Program is designed to reward your growth and contributions. +
+ Credits can be redeemed for watermark removal. +
+ In the future, credits may be used to redeem advanced template features. +

How to Earn Credits?

-

Welcome Bonus

-

All new users receive a bonus of 500 credits upon successful registration!

-
- -

Invite & Earn

-

Invite friends to join using your unique referral link. Both you and your friend will get 500 credits once they successfully sign up.

- -
-

If your invited friend completes their first purchase, you will receive a bonus equal to 20% of the credits they earn from that purchase.

-
-
- -
-

Daily Login

-

Starting the day after registration, log in daily to claim 100 credits.

-

This reward can be claimed for 7 consecutive days.

- -
-

Please note: Daily login credits will reset automatically on the 8th day, so remember to use them in time!

-
+

Invite friends to join using your unique referral link.

+

You will get 500 credits once they successfully sign in.

+
+

When your invited friends complete their first purchase, you will receive a 20% share of the credits they earn from that purchase.

diff --git a/components/layout/top-bar.tsx b/components/layout/top-bar.tsx index 9120912..c54fb43 100644 --- a/components/layout/top-bar.tsx +++ b/components/layout/top-bar.tsx @@ -380,7 +380,7 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe

{/* Sign-in entry */} -
+ {/*
-
+
*/} {/* AI 积分 */} From c38b6ad4838f20b9f724d2257ec36989fbab6e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sat, 11 Oct 2025 20:47:15 +0800 Subject: [PATCH 3/7] Enhance styling and structure of ChatInputBox and PcTemplateModal components; add custom scrollbar styles and improve layout responsiveness. --- app/globals.css | 35 +- components/ChatInputBox/H5TemplateDrawer.tsx | 2 +- components/ChatInputBox/PcTemplateModal.tsx | 782 +++++++++--------- components/script-renderer/ScriptRenderer.tsx | 12 +- 4 files changed, 432 insertions(+), 399 deletions(-) diff --git a/app/globals.css b/app/globals.css index 32576a1..a3954c9 100644 --- a/app/globals.css +++ b/app/globals.css @@ -307,6 +307,39 @@ body { } } -.mobile-textarea, .mobile-input { +.mobile-textarea, +.mobile-input { font-size: 16px !important; +} + +.custom-scrollbar { + scrollbar-width: thin; + scrollbar-color: transparent transparent; +} + +.custom-scrollbar:hover { + scrollbar-color: rgba(156, 163, 175, 0.2) rgba(0, 0, 0, 0); +} + +/* Webkit browsers (Chrome, Safari) */ +.custom-scrollbar::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.custom-scrollbar::-webkit-scrollbar-track { + background: transparent; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: transparent; + border-radius: 3px; +} + +.custom-scrollbar:hover::-webkit-scrollbar-thumb { + background: rgba(156, 163, 175, 0.5); +} + +.custom-scrollbar:hover::-webkit-scrollbar-thumb:hover { + background: rgba(156, 163, 175, 0.7); } \ No newline at end of file diff --git a/components/ChatInputBox/H5TemplateDrawer.tsx b/components/ChatInputBox/H5TemplateDrawer.tsx index 8f2bea5..aa4cff1 100644 --- a/components/ChatInputBox/H5TemplateDrawer.tsx +++ b/components/ChatInputBox/H5TemplateDrawer.tsx @@ -570,7 +570,7 @@ export const H5TemplateDrawer = ({ data-alt="items-section-title" className="text-base font-semibold text-white mb-3" > - input Configuration + Input Configuration