Compare commits
3 Commits
63784ed5f2
...
2d6ad73e1b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d6ad73e1b | ||
|
|
6112699133 | ||
|
|
9270691012 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,4 +4,4 @@ dist/
|
||||
coverage/
|
||||
.DS_Store
|
||||
*.log
|
||||
|
||||
.worktrees/
|
||||
|
||||
244
docs/plans/2026-03-21-upload-workbench-redesign-design.md
Normal file
244
docs/plans/2026-03-21-upload-workbench-redesign-design.md
Normal 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.
|
||||
336
docs/plans/2026-03-21-upload-workbench-redesign.md
Normal file
336
docs/plans/2026-03-21-upload-workbench-redesign.md
Normal 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"
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user