diff --git a/src/App.test.tsx b/src/App.test.tsx index acdb016..efd9131 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -15,12 +15,30 @@ describe('App bilingual UI', () => { expect(screen.getByLabelText('switch-ui-language-zh')).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByText('字幕语言')).toBeInTheDocument(); - expect(screen.getByText('上传视频')).toBeInTheDocument(); + expect(screen.getAllByText('上传视频').length).toBeGreaterThan(0); fireEvent.click(screen.getByLabelText('switch-ui-language-en')); expect(screen.getByLabelText('switch-ui-language-en')).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByText('Subtitle Language')).toBeInTheDocument(); - expect(screen.getByText('Upload Video')).toBeInTheDocument(); + expect(screen.getAllByText('Upload Video').length).toBeGreaterThan(0); + }); + + it('shows the upload workbench header and keeps the language switcher visible', () => { + render(); + + expect(screen.getByRole('banner')).toBeInTheDocument(); + expect(screen.getByLabelText('switch-ui-language-zh')).toBeInTheDocument(); + expect(screen.getByLabelText('switch-ui-language-en')).toBeInTheDocument(); + + fireEvent.click(screen.getByLabelText('switch-ui-language-en')); + + expect(screen.getByRole('heading', { name: 'Upload & prepare' })).toBeInTheDocument(); + expect( + screen.getByText( + 'Upload a source video, tune subtitle defaults, and choose dubbing settings before generation.', + ), + ).toBeInTheDocument(); + expect(screen.getByText('Video Translate')).toBeInTheDocument(); }); }); diff --git a/src/App.tsx b/src/App.tsx index c7e8b25..4c64240 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -38,51 +38,82 @@ function AppContent() { setCurrentView('editor'); }; + const languageSwitcher = ( +
+ + +
+ ); + return ( -
-
-
-
+
+ {currentView === 'upload' ? ( +
- - -
-
+
+
+ + {m.upload.workbenchEyebrow} + + {m.app.productName} +
+
+

+ {m.upload.workbenchTitle} +

+

+ {m.upload.workbenchDescription} +

+
+
+ +
{languageSwitcher}
+ + ) : ( +
{languageSwitcher}
+ )} +
+ +
+ {currentView === 'upload' ? ( + + ) : ( + setCurrentView('upload')} + /> + )}
- {currentView === 'upload' ? ( - - ) : ( - setCurrentView('upload')} - /> - )}
); } diff --git a/src/components/UploadScreen.test.tsx b/src/components/UploadScreen.test.tsx index 7c55792..ba2f248 100644 --- a/src/components/UploadScreen.test.tsx +++ b/src/components/UploadScreen.test.tsx @@ -56,6 +56,17 @@ describe('UploadScreen', () => { expect(preview).toHaveStyle({ fontSize: '32px', bottom: '18%' }); }); + it('renders upload, mode, subtitle defaults, and language cards inside the responsive workbench', () => { + renderUploadScreen(); + + expect(screen.getByTestId('upload-workbench')).toBeInTheDocument(); + expect(screen.getByTestId('upload-dropzone-card')).toBeInTheDocument(); + expect(screen.getByTestId('upload-settings-column')).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Mode & Workflow' })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Subtitle Defaults' })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: 'Language & Dubbing' })).toBeInTheDocument(); + }); + it('shows a fixed English subtitle language and the supported TTS languages', () => { renderUploadScreen(); @@ -125,4 +136,12 @@ describe('UploadScreen', () => { expect(screen.queryByRole('button', { name: /close trim/i })).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: /generate translated video/i })).toBeDisabled(); }); + + it('keeps the upload interaction wired through the redesigned dropzone card', () => { + renderUploadScreen(); + + expect(screen.getByTestId('upload-dropzone-card')).toContainElement( + screen.getByLabelText(/upload video file/i), + ); + }); }); diff --git a/src/components/UploadScreen.tsx b/src/components/UploadScreen.tsx index cd31dc9..4d9dadb 100644 --- a/src/components/UploadScreen.tsx +++ b/src/components/UploadScreen.tsx @@ -33,6 +33,10 @@ export default function UploadScreen({ const [tempFile, setTempFile] = useState(null); const [subtitleDefaults, setSubtitleDefaults] = useState(DEFAULT_SUBTITLE_DEFAULTS); const fileInputRef = useRef(null); + const activeModeLabel = mode === 'editing' ? m.upload.editingMode : m.upload.simpleMode; + const activeModeDescription = mode === 'editing' ? m.upload.editingModeDesc : m.upload.simpleModeDesc; + const settingsCardClass = + 'app-surface rounded-[28px] border border-white/70 px-5 py-5 shadow-[0_18px_60px_-34px_rgba(15,23,42,0.3)] sm:px-6'; const clearPendingUpload = () => { setTempFile(null); @@ -55,91 +59,145 @@ export default function UploadScreen({ }; return ( -
- {/* Left: Upload Area */} -
-
- - -

{m.upload.clickToUpload}

- -
-

- {m.upload.supportedFormats} -

-
- - {/* Right: Settings */} -
- {/* Mode Selection */} -
-
setMode('editing')} - > -
- {mode === 'editing' ? ( - - ) : ( - - )} - {m.upload.editingMode} -
-

{m.upload.editingModeDesc}

+
+
+
+
+

+ {m.upload.uploadPanelTitle} +

+

+ {m.upload.uploadPanelDescription} +

-
setMode('simple')} - > -
- {mode === 'simple' ? ( - - ) : ( - - )} - {m.upload.simpleMode} -
-

{m.upload.simpleModeDesc}

+ +
+

+ {m.upload.modeWorkflowTitle} +

+

{activeModeLabel}

+

{activeModeDescription}

-
-
-
-

{m.upload.subtitleDefaults}

-

- {m.upload.subtitleDefaultsDesc} +

+
+
+ + +
+
+ +
+

{m.upload.uploadVideo}

+

+ {m.upload.clickToUpload}

+ +
+
+
+ +
+

{m.upload.supportedFormats}

+
+ {activeModeLabel} +
+
+
+ + {showTrimModal && tempFile && (