video_translate/docs/plans/2026-03-21-upload-first-fold-layout.md
2026-03-21 13:56:16 +08:00

264 lines
7.6 KiB
Markdown

# Upload First-Fold Layout Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Compress the upload workbench so the complete upload workflow remains visible in the first desktop viewport without forcing users to scroll to discover mode, subtitle, and dubbing options.
**Architecture:** Keep the existing upload screen state and action flow intact, but reduce the upload page's vertical footprint. Most of the work stays inside `src/App.tsx` and `src/components/UploadScreen.tsx`, with supporting text updates in `src/i18n.tsx`, one small surface helper in `src/index.css`, and test updates in the existing app/upload test files. The tall TTS list will be replaced with a compact grid so all supported language choices are immediately visible.
**Tech Stack:** React 19, TypeScript, Tailwind CSS v4 utilities, Vitest, React Testing Library
---
### Task 1: Lock the compact first-fold language layout with failing tests
**Files:**
- Modify: `src/components/UploadScreen.test.tsx`
- Reference: `src/components/UploadScreen.tsx`
**Step 1: Write the failing test**
Add a test that describes the compact language section and rejects the old tabbed list structure:
```tsx
it('shows all supported tts languages in a compact always-visible grid', () => {
renderUploadScreen();
expect(screen.getByTestId('tts-language-grid')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Chinese' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Cantonese' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'English' })).toBeInTheDocument();
expect(screen.queryByText('ABC')).not.toBeInTheDocument();
expect(screen.queryByText('GHI')).not.toBeInTheDocument();
expect(screen.queryByText('DEF')).not.toBeInTheDocument();
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
```
Expected: FAIL because the upload page still renders the alphabet tabs and has no compact grid marker.
**Step 3: Write minimal implementation**
Update `src/components/UploadScreen.tsx` just enough to:
- remove the old alphabet tabs
- expose a compact language grid container with `data-testid="tts-language-grid"`
- keep the existing language buttons and selection behavior
**Step 4: Run test to verify it passes**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
```
Expected: PASS for the new compact-language assertion.
**Step 5: Commit**
```bash
git add src/components/UploadScreen.tsx src/components/UploadScreen.test.tsx
git commit -m "test: lock compact tts language layout"
```
### Task 2: Compress the upload page shell and card heights
**Files:**
- Modify: `src/App.tsx`
- Modify: `src/components/UploadScreen.tsx`
- Modify: `src/i18n.tsx`
- Modify: `src/index.css`
- Test: `src/App.test.tsx`
**Step 1: Write the failing test**
Add an app-level test that captures the compact first-fold shell:
```tsx
it('renders the upload shell with compact first-fold copy', () => {
render(<App />);
fireEvent.click(screen.getByLabelText('switch-ui-language-en'));
expect(screen.getByRole('heading', { name: 'Upload & prepare' })).toBeInTheDocument();
expect(screen.getByText('Everything you need to start translation is visible right away.')).toBeInTheDocument();
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
```
Expected: FAIL because the compact shell copy does not exist yet.
**Step 3: Write minimal implementation**
Update the upload shell and localized copy to reduce the first-fold height budget:
- shrink upload-header padding and margins
- reduce title and description footprint
- tighten workbench spacing
- keep the language switcher visible
Localized copy example:
```tsx
workbenchDescription: 'Everything you need to start translation is visible right away.'
```
**Step 4: Run test to verify it passes**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
```
Expected: PASS for the compact-header assertion.
**Step 5: Commit**
```bash
git add src/App.tsx src/App.test.tsx src/i18n.tsx src/index.css
git commit -m "feat: compress upload shell for first-fold visibility"
```
### Task 3: Tighten the upload card and settings cards to fit the first fold
**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 targeted structure checks for the compact card composition:
```tsx
it('keeps the compact upload controls and primary action visible together', () => {
renderUploadScreen();
expect(screen.getByTestId('upload-dropzone-card')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Upload Video' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Generate Translated Video' })).toBeInTheDocument();
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
```
Expected: FAIL until the compact layout and new language grid are fully in place.
**Step 3: Write minimal implementation**
Refactor `src/components/UploadScreen.tsx` to reclaim vertical space:
- reduce upload-card header spacing
- shorten the dropzone min height
- reduce top and bottom padding on all cards
- tighten card gaps
- shrink subtitle preview height
- compress mode buttons
- render the TTS language options in a multi-column grid instead of a scrolling letter list
- keep the generate button at the bottom of the visible settings stack
Representative shape:
```tsx
<aside className="grid gap-4">
<section className="...compact-mode-card..." />
<section className="...compact-subtitle-card..." />
<section className="...compact-language-card...">
<div data-testid="tts-language-grid" className="grid grid-cols-2 gap-2">
...
</div>
</section>
</aside>
```
**Step 4: Run test to verify it passes**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
```
Expected: PASS for the compact-card and compact-language assertions while preserving existing upload behavior tests.
**Step 5: Commit**
```bash
git add src/components/UploadScreen.tsx src/components/UploadScreen.test.tsx src/index.css src/i18n.tsx
git commit -m "feat: fit upload workbench into first fold"
```
### Task 4: Full verification
**Files:**
- Modify: `src/App.tsx`
- Modify: `src/App.test.tsx`
- Modify: `src/components/UploadScreen.tsx`
- Modify: `src/components/UploadScreen.test.tsx`
- Modify: `src/i18n.tsx`
- Modify: `src/index.css`
**Step 1: Write the failing test**
If any final localized copy or structure assertions remain missing after Tasks 1-3, add the smallest regression test needed before the last polish.
**Step 2: Run test to verify it fails**
Run:
```bash
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
```
Expected: FAIL only if the final regression gap is real.
**Step 3: Write minimal implementation**
Apply only the last polish needed to satisfy the failing regression test, without broadening scope.
**Step 4: Run test to verify it passes**
Run:
```bash
npm.cmd test
npm.cmd run lint
npm.cmd run build
```
Expected:
- all tests PASS
- typecheck PASS
- build PASS
**Step 5: Commit**
```bash
git add src/App.tsx src/App.test.tsx src/components/UploadScreen.tsx src/components/UploadScreen.test.tsx src/i18n.tsx src/index.css
git commit -m "test: verify first-fold upload layout"
```