Files
gui-video-clipper/src/lib/utils/time.ts
cottongin c1de18715e feat: v0.2.1 — Player controls polish, screenshot capture, quit shortcut
v0.2.0 introduced Player mode with floating controls, auto-hide UI,
PiP, fullscreen, JKL shuttle, speed selector, captions, and cut-clip.

v0.2.1 builds on that with extensive UI polish and new features:

Player Controls Polish:
- Enlarged center playback icons (Play 48, Rewind/FF 36) with filled SVGs
- Moved Cut Clip scissors into center group with hidden cut-before button
- Narrowed floating panel from 70% to 60% width
- Custom-styled volume slider matching seek bar (linear-gradient fill)
- Pure opacity fade animation (0.35s) replacing slide-in
- Tinted panel background (rgba(53,54,80,0.75)) visible on black canvas
- Drop shadow on center transport controls
- Scissors buttons dimmed via color (not opacity) with single-property transitions
- ScissorsLineDashed icon for cut-before variant
- Timestamps in single seek row (time | bar | duration) with sans-serif font
- Milliseconds shown at smaller size, revealed on hover

Screenshot Capture (S / Shift+S):
- Canvas compositing at native video resolution with optional caption burn-in
- Caption rendering matches CSS preview (font, background, outline, word highlight)
- New Rust save_screenshot command using existing filename pattern
- Output: {title} - {ms}ms-screenshot.png
- crossorigin=anonymous on video element for untainted canvas

Other:
- Q keyboard shortcut to quit via Tauri process plugin
- formatTimeParts() utility for split timestamp rendering
- Updated About dialog shortcuts list
- Version bump to 0.2.1

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 05:30:45 -04:00

51 lines
1.7 KiB
TypeScript

export function formatTime(totalSeconds: number): string {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
const millis = Math.round((totalSeconds % 1) * 1000);
const mm = String(minutes).padStart(2, '0');
const ss = String(seconds).padStart(2, '0');
const ms = String(millis).padStart(3, '0');
if (hours > 0) {
return `${hours}:${mm}:${ss}.${ms}`;
}
return `${mm}:${ss}.${ms}`;
}
export function formatTimeParts(totalSeconds: number): { main: string; ms: string } {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
const millis = Math.round((totalSeconds % 1) * 1000);
const mm = String(minutes).padStart(2, '0');
const ss = String(seconds).padStart(2, '0');
const ms = String(millis).padStart(3, '0');
const main = hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
return { main, ms: `.${ms}` };
}
export function parseTime(str: string): number | null {
const hmsMatch = str.match(/^(\d+):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/);
if (hmsMatch) {
const h = parseInt(hmsMatch[1], 10);
const m = parseInt(hmsMatch[2], 10);
const s = parseInt(hmsMatch[3], 10);
const ms = hmsMatch[4] ? parseInt(hmsMatch[4].padEnd(3, '0'), 10) : 0;
return h * 3600 + m * 60 + s + ms / 1000;
}
const msMatch = str.match(/^(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/);
if (msMatch) {
const m = parseInt(msMatch[1], 10);
const s = parseInt(msMatch[2], 10);
const ms = msMatch[3] ? parseInt(msMatch[3].padEnd(3, '0'), 10) : 0;
return m * 60 + s + ms / 1000;
}
return null;
}