10 KiB
Upload Workbench Redesign Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Rebuild the upload page into a responsive workbench layout that keeps the settings panel fully accessible while preserving the current upload, trim, subtitle-default, and TTS selection behavior.
Architecture: Keep the existing upload-page workflow and state model, but replace the rigid centered flex layout with a top-aligned page shell and responsive grid. Most of the work lives in src/components/UploadScreen.tsx, with a smaller shell adjustment in src/App.tsx and assertion updates in the upload-page tests. The implementation should stay behavior-compatible and lean on CSS/Tailwind layout changes instead of introducing new product state.
Tech Stack: React 19, TypeScript, Vite, Tailwind CSS v4 utilities, Lucide React icons, Vitest, React Testing Library
Task 1: Lock the redesigned upload-page structure with failing tests
Files:
- Modify:
src/App.test.tsx - Modify:
src/components/UploadScreen.test.tsx - Reference:
src/App.tsx - Reference:
src/components/UploadScreen.tsx
Step 1: Write the failing test
Add assertions that describe the redesigned shell and card structure before implementation:
it('shows the upload workbench header and keeps the language switcher visible', () => {
render(<App />);
expect(screen.getByText('Video Translate')).toBeInTheDocument();
expect(
screen.getByText('Upload a source video, tune subtitle defaults, and choose dubbing settings before generation.'),
).toBeInTheDocument();
expect(screen.getByLabelText('switch-ui-language-zh')).toBeInTheDocument();
expect(screen.getByLabelText('switch-ui-language-en')).toBeInTheDocument();
});
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();
});
Step 2: Run test to verify it fails
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
Expected: FAIL because the new header copy, section headings, and test ids do not exist yet.
Step 3: Write minimal implementation
Do not redesign everything yet. Add only the smallest missing semantic hooks and copy needed to satisfy the new tests:
- app-shell heading and intro copy in
src/App.tsx - stable
data-testidmarkers and section headings insrc/components/UploadScreen.tsx
Use copy aligned with the approved design:
<section aria-label="upload-page-intro">
<p>Video Translate</p>
<h1>{m.upload.workbenchTitle}</h1>
<p>{m.upload.workbenchDescription}</p>
</section>
Step 4: Run test to verify it passes
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
Expected: PASS for the new structure assertions, with no unrelated failures in those files.
Step 5: Commit
git add src/App.test.tsx src/components/UploadScreen.test.tsx src/App.tsx src/components/UploadScreen.tsx src/i18n.tsx
git commit -m "test: lock upload workbench shell structure"
Task 2: Rebuild the page shell and header layout
Files:
- Modify:
src/App.tsx - Modify:
src/i18n.tsx - Test:
src/App.test.tsx
Step 1: Write the failing test
Extend the app test to assert the header content is grouped into a page shell and remains bilingual:
expect(screen.getByRole('banner')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Upload & prepare' })).toBeInTheDocument();
expect(screen.getByText('Keep subtitle defaults visible and avoid clipped settings panels.')).toBeInTheDocument();
Step 2: Run test to verify it fails
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
Expected: FAIL because the new banner structure and localized hero text are not present yet.
Step 3: Write minimal implementation
Update src/App.tsx and src/i18n.tsx to:
- wrap the upload page in a proper shell with
min-h-screen - add a responsive header above the view content
- move the language switcher into the header
- add localized upload-page title and description strings for both Chinese and English
Implementation sketch:
<div className="min-h-screen bg-[...gradient...] text-slate-900">
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
<header className="mb-8 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
<div className="space-y-3">
<p className="...">{m.app.productName}</p>
<h1 className="...">{m.upload.workbenchTitle}</h1>
<p className="...">{m.upload.workbenchDescription}</p>
</div>
{languageSwitcher}
</header>
{currentView === 'upload' ? <UploadScreen ... /> : <EditorScreen ... />}
</div>
</div>
Step 4: Run test to verify it passes
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
Expected: PASS, showing the new header shell in the upload view and preserving the language toggle behavior.
Step 5: Commit
git add src/App.tsx src/i18n.tsx src/App.test.tsx
git commit -m "feat: add upload page workbench shell"
Task 3: Replace the rigid upload-page flex layout with a responsive workbench grid
Files:
- Modify:
src/components/UploadScreen.tsx - Modify:
src/components/UploadScreen.test.tsx - Modify:
src/index.css - Reference:
src/types.ts
Step 1: Write the failing test
Add tests that lock in the redesigned composition while preserving behavior:
it('keeps subtitle preview behavior after the layout redesign', () => {
renderUploadScreen();
fireEvent.change(screen.getByLabelText(/subtitle initial size/i), {
target: { value: '32' },
});
expect(screen.getByTestId('upload-subtitle-preview')).toHaveStyle({ fontSize: '32px' });
expect(screen.getByRole('button', { name: 'Generate Translated Video' })).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),
);
});
Step 2: Run test to verify it fails
Run:
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
Expected: FAIL because the current layout does not expose the new workbench structure markers and updated headings yet.
Step 3: Write minimal implementation
Refactor src/components/UploadScreen.tsx to:
- replace the outer
flex ... h-screen items-centershell with a responsive grid container - use a wide primary upload card plus a flexible sidebar column
- remove the fixed
w-[400px]sidebar - add
min-w-0to shrinkable containers - convert the settings area into three distinct cards:
- mode and workflow
- subtitle defaults
- language and dubbing
- reduce the preview height and tighten card spacing
- keep the language list internally scrollable
- keep the primary generation button prominent at the bottom of the settings flow
Representative JSX shape:
<section
data-testid="upload-workbench"
className="grid gap-6 xl:grid-cols-[minmax(0,1.65fr)_minmax(320px,420px)]"
>
<div data-testid="upload-dropzone-card" className="min-w-0 ...">
...
</div>
<aside data-testid="upload-settings-column" className="min-w-0 space-y-5 xl:sticky xl:top-6">
<section className="...">
<h2>{m.upload.modeWorkflowTitle}</h2>
...
</section>
<section className="...">
<h2>{m.upload.subtitleDefaults}</h2>
...
</section>
<section className="...">
<h2>{m.upload.languageDubbingTitle}</h2>
...
</section>
</aside>
</section>
If a small amount of shared CSS improves consistency, keep it narrowly scoped in src/index.css:
.app-surface {
background: rgba(255, 255, 255, 0.86);
backdrop-filter: blur(14px);
}
Step 4: Run test to verify it passes
Run:
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
Expected: PASS for the upload-screen interaction tests, including subtitle-preview updates and upload confirmation behavior.
Step 5: Commit
git add src/components/UploadScreen.tsx src/components/UploadScreen.test.tsx src/index.css src/i18n.tsx
git commit -m "feat: redesign upload page workbench layout"
Task 4: Full verification and cleanup
Files:
- Modify:
src/App.tsx - Modify:
src/components/UploadScreen.tsx - Modify:
src/i18n.tsx - Modify:
src/index.css - Test:
src/App.test.tsx - Test:
src/components/UploadScreen.test.tsx
Step 1: Write the failing test
Add one final regression assertion for the bilingual header text to ensure the redesigned shell still switches languages correctly:
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();
Step 2: Run test to verify it fails
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
Expected: FAIL until the new localized header copy is fully wired.
Step 3: Write minimal implementation
Complete any remaining polish needed to satisfy the regression coverage:
- finalize localized header and section labels
- ensure the upload page stays visually coherent in both locales
- remove unused classes or stale layout wrappers from the old design
Step 4: Run test to verify it passes
Run:
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
npm run lint
npm run build
Expected:
- targeted tests PASS
- typecheck PASS
- production build PASS
Step 5: Commit
git add src/App.tsx src/components/UploadScreen.tsx src/i18n.tsx src/index.css src/App.test.tsx src/components/UploadScreen.test.tsx
git commit -m "test: verify upload workbench redesign"