Compare commits

...

3 Commits

Author SHA1 Message Date
Song367
2d6ad73e1b chore: ignore local worktree directory
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 32s
2026-03-21 12:45:11 +08:00
Song367
6112699133 docs: add upload workbench redesign plan 2026-03-21 12:39:57 +08:00
Song367
9270691012 docs: add upload workbench redesign design 2026-03-21 12:38:48 +08:00
3 changed files with 581 additions and 1 deletions

2
.gitignore vendored
View File

@ -4,4 +4,4 @@ dist/
coverage/
.DS_Store
*.log
.worktrees/

View File

@ -0,0 +1,244 @@
# Upload Workbench Redesign Design
**Goal:** Redesign the upload page so the right-side controls are always accessible, the layout adapts cleanly across viewport widths, and the page feels like a coherent product workbench instead of a squeezed form.
## Context
The current upload page uses a fixed two-column flex layout inside a constrained `max-w-6xl` shell. The left upload area grows aggressively while the right settings column is locked to `w-[400px]`. Combined with `h-screen`, centered alignment, and large outer padding, the layout runs out of horizontal space quickly and the right column becomes partially or fully clipped on narrower desktop windows.
Functionally, the page already collects the right information:
- upload source video
- choose work mode
- define initial subtitle defaults
- choose TTS language
The redesign should preserve that workflow. This is a front-end layout and presentation change, not a product-flow rewrite.
## User-Approved Direction
The approved direction is:
- keep a desktop-friendly left/right split layout
- automatically stack into a top/bottom layout on narrower viewports
- preserve the current page flow and controls
- make the upload area the primary focal point
- keep settings visible, grouped, and readable without horizontal clipping
## Approaches Considered
### Option A: Responsive dual-column workbench
Keep the existing information architecture, but replace the rigid flex layout with a responsive grid that shifts from dual-column to single-column when space is limited.
**Pros**
- Solves the clipping problem directly
- Preserves current user workflow
- Requires changes mostly in the upload page shell and card structure
- Keeps settings visible without hiding them behind extra clicks
**Cons**
- Requires reworking spacing, hierarchy, and card composition
### Option B: Settings drawer
Collapse the right-side controls into a slide-over drawer triggered by a button.
**Pros**
- Eliminates horizontal overflow entirely
- Can simplify the initial page appearance
**Cons**
- Reduces discoverability of settings
- Adds an extra interaction before upload
- Makes subtitle default preview less immediately useful
### Option C: Multi-step wizard
Split upload and settings into separate steps.
**Pros**
- Very clear progressive flow
- Maximum focus per step
**Cons**
- Changes the product flow more than requested
- Adds more navigation state
- Unnecessary for the current page complexity
### Recommendation
Use **Option A**. It fixes the visibility problem without changing the product's mental model.
## Architecture
### Page Shell
Replace the current `h-screen` centered flex shell with a top-aligned `min-h-screen` workbench container. The page should use:
- a lightweight header area with page title, support copy, and language switcher
- a responsive content grid for the upload page body
- natural document scrolling instead of forcing the full screen height
This removes the clipping pressure caused by vertical centering and fixed-height layout assumptions.
### Responsive Grid
The main upload page body should use a grid with these rules:
- large desktop: two columns with a wide primary upload column and a narrower settings column
- medium desktop: still two columns, but with reduced gap and a flexible sidebar width
- tablet and below: a single-column stack with the upload card first and the settings cards below
Implementation should avoid fixed-width overflow by:
- using `minmax(...)` for the sidebar column instead of a hard-coded width
- adding `min-w-0` to grid children so nested content can shrink safely
- limiting card internals rather than the full page height
### Visual Hierarchy
The page should read as a workbench with one primary action and three supporting settings groups:
1. Upload area
2. Mode selection
3. Subtitle defaults
4. Language and dubbing settings
The upload card becomes the visual anchor of the page. The settings column becomes a structured sidebar of related control cards rather than a tall wall of similar white panels.
### Header
Move the language switcher into a clear page header that also introduces the page. The header should include:
- a concise title
- one sentence explaining the workflow
- the existing bilingual switcher aligned to the right on larger screens and wrapping naturally on smaller screens
This removes the current disconnected top-right floating control feeling.
## Layout Composition
### Upload Card
The upload card should be the largest surface in the page. It contains:
- a strong heading and short instruction copy
- the drag-and-drop / click target
- supported format text
- optional contextual mode summary
The invisible file input should still power the interaction, but the visible button and dropzone should feel like one unified action surface.
### Mode Card
Keep the two work modes, but restyle them as a cleaner segmented card section:
- each option uses consistent spacing and iconography
- selected state is obvious through background, border, and copy contrast
- the section remains compact enough for narrower widths
### Subtitle Defaults Card
Preserve the existing subtitle preview and sliders, but make the card more compact and better balanced:
- preview area becomes shorter vertically
- reset action remains in the header
- control spacing is tightened
- the preview remains visually useful without dominating the sidebar
### Language and Dubbing Card
Keep subtitle language and TTS language selection in one grouped card. The card should:
- present subtitle language as read-only contextual information
- keep the alphabet/popular tabs compact
- constrain the language list with internal scrolling
- pin the primary action button near the bottom of the card flow
On desktop, the settings column should use `sticky` behavior so the user can keep context while scrolling the page.
## Responsive Behavior
### Large Desktop
- two-column layout
- upload card and settings sidebar visible simultaneously
- settings sidebar can remain sticky
### Medium Desktop
- still two columns
- reduced outer padding and inter-column gap
- sidebar width narrows within safe limits
### Narrow Desktop / Tablet
- single-column stack
- upload card first
- settings cards follow underneath
- sticky behavior disabled
### Mobile-Like Widths
Although the current use case is desktop-heavy, the layout should remain readable:
- all cards become full-width
- header content wraps cleanly
- button actions span full width where helpful
- no content should overflow horizontally
## Interaction Details
### Primary Action Clarity
There should be one clearly dominant action:
- before a file is selected, the upload surface is primary
- after a file is prepared, the final "Generate Translated Video" action remains the strongest button in the settings flow
The redesign should reduce the feeling of multiple competing calls to action.
### Scroll Ownership
The full page should scroll naturally. Only the TTS language list should own an internal scroll region. The page should avoid nested full-height scroll containers competing with each other.
### Behavior Preservation
The redesign must not change:
- the `onUpload(...)` contract
- subtitle default values or payload structure
- selected TTS language mapping
- trim modal flow
## Testing Strategy
Add or update tests to cover:
- the upload page structure still renders all core controls after the redesign
- the language switcher remains available from the upload page shell
- subtitle preview still reflects size and bottom-offset changes
- upload confirmation still forwards the same payload values
Because responsive behavior is mostly CSS-driven, automated tests should focus on stable structural markers and interaction continuity. Final visual validation should also include a manual browser check across multiple viewport widths.
## Out of Scope
This redesign does not include:
- changing the upload-to-editor workflow
- adding new subtitle styling options
- changing supported languages
- redesigning the editor screen
- altering backend request contracts
## Rollout Notes
This work should remain contained to the upload experience and its tests. The main success criterion is simple: at the viewport width shown in the reported issue, the right-side controls must remain fully accessible without horizontal clipping, while the overall page looks more intentional and usable.

View File

@ -0,0 +1,336 @@
# 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:
```tsx
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:
```bash
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-testid` markers and section headings in `src/components/UploadScreen.tsx`
Use copy aligned with the approved design:
```tsx
<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:
```bash
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**
```bash
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:
```tsx
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:
```bash
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:
```tsx
<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:
```bash
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**
```bash
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:
```tsx
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:
```bash
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-center` shell 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-0` to 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:
```tsx
<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`:
```css
.app-surface {
background: rgba(255, 255, 255, 0.86);
backdrop-filter: blur(14px);
}
```
**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 upload-screen interaction tests, including subtitle-preview updates and upload confirmation behavior.
**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: 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:
```tsx
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:
```bash
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:
```bash
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**
```bash
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"
```