docs: add v0.1.3 polish/tweaks design spec

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 16:32:17 -04:00
parent 9499d776fd
commit 9752a6d614

View File

@@ -0,0 +1,198 @@
# v0.1.3 Polish & Tweaks — Design Spec
**Date:** 2026-09-22
**Version:** 0.1.2 → 0.1.3
## Overview
Three UI polish features for v0.1.3: adjustable clip-list layout, improved timeline timestamp labels with a mouse-proximity magnification effect, and a custom right-click context menu on the timeline.
---
## Feature 1: Adjustable Layout (Clip List Position)
### Current State
The main content area is a vertical stack:
```
┌──────────────────────────────┐
│ Toolbar │
├──────────────────────────────┤
│ Video Preview │
├──────────────────────────────┤
│ Transport Controls │
├──────────────────────────────┤
│ Timeline / Thumbs / Waveform │
├──────────────────────────────┤
│ Clip List │
├──────────────────────────────┤
│ Status Bar │
└──────────────────────────────┘
```
### Proposed: Two Layout Modes
A new `clipListPosition` preference (`'bottom' | 'left'`) controls where the clip list appears. Default: `'bottom'` (current behavior, unchanged).
When set to `'left'`, the layout becomes:
```
┌──────────────────────────────────────────┐
│ Toolbar │
├──────────┬───────────────────────────────┤
│ Clip │ Video Preview │
│ List │ │
│ │ │
│ ├───────────────────────────────┤
│ │ Transport Controls │
├──────────┴───────────────────────────────┤
│ Timeline / Thumbs / Waveform (full) │
├──────────────────────────────────────────┤
│ Status Bar │
└──────────────────────────────────────────┘
```
Key details:
- The clip list shares horizontal space only with the video preview and transport controls. The timeline remains full-width underneath — no horizontal space is sacrificed for timeline real estate.
- A vertical drag-to-resize handle sits between the clip list and the video area, allowing the user to adjust the sidebar width. Default width: ~220px. Min: 150px. Max: 40% of window width.
- The existing vertical resize handle between timeline and clip list only appears in `'bottom'` mode. In `'left'` mode, the timeline height is controlled by the existing `timelineHeight` state (still resizable via a horizontal drag handle between the upper section and the timeline).
### Toggle Button
A button in the toolbar (alongside ⚙ and ℹ) toggles between `'bottom'` and `'left'` modes. The icon visually hints at the current layout — e.g. a stacked-rows icon for bottom mode, a sidebar icon for left mode. Clicking it switches immediately and persists the preference.
### Persistence
- Add `clipListPosition: 'bottom' | 'left'` to the `Preferences` interface in `preferences.svelte.ts`.
- Add `clipListWidth: number` (default 220) for the sidebar width in left mode.
- Both are saved/loaded via the existing Tauri store mechanism.
### Implementation Notes
- `App.svelte` is the only component that needs structural changes. `ClipList.svelte`, `VideoPlayer.svelte`, and `Timeline.svelte` remain unchanged — they just get placed in different containers.
- In `'left'` mode, the `.content` area becomes:
- A flex column with two children:
1. **Upper section** (flex row): `[ClipList sidebar | flex column of [VideoPlayer, TransportControls]]`
2. **Lower section** (same as before): timeline pane with resize handle
- The vertical resize handle between the upper and lower sections works the same as the existing one.
- In `'bottom'` mode: the current layout is preserved exactly.
- The `ClipList` component needs no changes; it already fills its container. The only adjustment is removing the `border-top` when in left mode (the sidebar should have a `border-right` instead), which can be handled via a CSS class or prop.
---
## Feature 2: Timeline Timestamp Labels
### Current State
Timestamp labels are rendered at `10px` font size in `drawTimeTicks()` in `renderer.ts`. They are small and hard to read.
### Proposed Changes
1. **Increase base font size** from `10px` to `12px`.
2. **Mouse-proximity magnification effect:** As the cursor moves along the timeline, labels near the cursor smoothly scale up. This creates an invisible "magnifying lens" effect for timestamps.
### Magnification Behavior
- **Radius:** ~80px from cursor (in canvas pixel space).
- **Peak size:** ~18px directly under the cursor.
- **Falloff:** Smooth cosine interpolation from peak to base size. A label 80px away renders at base size; a label directly under the cursor renders at peak size.
- **Vertical alignment:** Labels stay bottom-aligned as they grow — the text baseline remains constant, so larger labels grow upward. This prevents layout jitter.
- **Cursor leaves timeline:** When the mouse leaves the canvas (`onmouseleave`), `mouseX` is set to `null` and all labels render at the base size. No magnification in this state.
### Implementation Notes
- `Timeline.svelte` already handles `onmousemove`. Add tracking of the mouse X coordinate (a simple `let mouseX = $state<number | null>(null)`), updated on `mousemove` and cleared on `mouseleave`.
- Pass `mouseX` into `drawTimeline()` → `drawTimeTicks()`.
- `drawTimeTicks()` computes the per-label font size based on distance from `mouseX`:
```
distance = abs(labelX - mouseX)
if distance > MAGNIFY_RADIUS: scale = 0
else: scale = (1 + cos(PI * distance / MAGNIFY_RADIUS)) / 2
fontSize = BASE_FONT_SIZE + BONUS_FONT_SIZE * scale
```
- Trigger a redraw whenever `mouseX` changes. Since `requestDraw()` already coalesces via `requestAnimationFrame`, this adds no extra frame overhead — it simply piggybacks on the next scheduled frame.
---
## Feature 3: Timeline Right-Click Context Menu
### Current State
Right-clicking the timeline shows the default web/Tauri context menu (Reload, Inspect Element), which is not useful.
### Proposed: Custom Context Menu
A styled context menu appears on right-click anywhere on the timeline canvas. Right-clicking also moves the playhead to the clicked position.
### Menu Items
| # | Label | Behavior | Availability |
|---|-------|----------|-------------|
| 1 | **Mark In** | Sets in-point at the right-clicked time (calls `markInPoint`) | Always |
| 2 | **Mark Out** | Sets out-point at the right-clicked time (calls `markOutPoint`) | Always |
| — | *(separator)* | | |
| 3 | **Go to Clip Start** | Seeks playhead to `selectedClip.startTime` | Disabled if no clip selected |
| 4 | **Go to Clip End** | Seeks playhead to `selectedClip.endTime` | Disabled if no clip selected |
| — | *(separator)* | | |
| 5 | **Delete Clip** | Removes the selected clip | Hidden if no clip selected |
### Behavior
- **Right-click on canvas:** `contextmenu` event is intercepted, default is prevented. The playhead moves to the clicked time position. The context menu appears at the cursor's screen coordinates.
- **Selecting an item:** Executes the action and closes the menu.
- **Closing without action:** Click anywhere outside the menu, press Escape, or scroll — the menu closes.
- **Positioning:** The menu is rendered as a positioned `<div>` appended at the `App.svelte` level (or uses a portal pattern) so it isn't clipped by any `overflow: hidden` containers. If the menu would extend beyond the window edge, it flips direction (e.g. opens leftward or upward).
### Component: `TimelineContextMenu.svelte`
Props:
- `x: number` — screen X position
- `y: number` — screen Y position
- `time: number` — the timeline time at the right-click position
- `selectedClipId: string | null` — to determine which items are enabled/hidden
- `onAction: (action: ContextMenuAction) => void` — callback for menu item selection
- `onClose: () => void` — callback to close the menu
Actions (discriminated union):
```typescript
type ContextMenuAction =
| { type: 'mark-in' }
| { type: 'mark-out' }
| { type: 'go-to-clip-start' }
| { type: 'go-to-clip-end' }
| { type: 'delete-clip' };
```
### Styling
- Dark theme matching the app's existing style (dark background, light text, hover highlights).
- Subtle border/shadow to float above the timeline.
- Disabled items: dimmed text, no hover effect, not clickable.
- Separators: thin horizontal lines using `var(--border)`.
### Implementation Notes
- `Timeline.svelte` adds an `oncontextmenu` handler on the canvas element.
- The handler computes the time from the click X coordinate (using existing `computeClickTime`), seeks the playhead, and emits an event / sets state to show the context menu.
- The context menu component is rendered conditionally in `Timeline.svelte` (or `App.svelte` if clipping is an issue).
- Keyboard shortcut hints can optionally be shown in the menu items (e.g. "Mark In I") for discoverability.
---
## Version Bump
All three locations updated from `0.1.2` → `0.1.3`:
- `package.json` — `version` field
- `src-tauri/tauri.conf.json` — `version` field
- `src-tauri/Cargo.toml` — `version` field
---
## Scope Boundaries
- No changes to the backend (Rust). All three features are purely frontend.
- No changes to clip store logic — existing `markInPoint`, `markOutPoint`, `removeClip`, `selectClip` functions are reused as-is.
- No new dependencies.
- The context menu does NOT appear on the minimap — only on the main timeline canvas.