From fac4405a587295ee90adb113fb554e14f32ae54f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 23:28:53 +0000 Subject: [PATCH] Make the smallest clip nudge 100ms instead of 10ms Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V1geD6GoCf1ZjN16BxcEu2 --- frontend/src/components/ClipEditor.test.tsx | 14 +++++++------- frontend/src/components/TimecodeInput.tsx | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/ClipEditor.test.tsx b/frontend/src/components/ClipEditor.test.tsx index e46cb63..ed53398 100644 --- a/frontend/src/components/ClipEditor.test.tsx +++ b/frontend/src/components/ClipEditor.test.tsx @@ -231,17 +231,17 @@ describe('ClipEditor, deleting a clip', () => { vi.spyOn(clipsApi, 'list').mockResolvedValue([]) const create = vi .spyOn(clipsApi, 'create') - .mockResolvedValue({ ...liveClip, id: 'c4', in_point: 1.5, out_point: 12.01 }) + .mockResolvedValue({ ...liveClip, id: 'c4', in_point: 1.5, out_point: 12.1 }) renderEditor() await userEvent.type(await screen.findByLabelText('In point seconds'), '1') await userEvent.type(screen.getByLabelText('In point milliseconds'), '500') await userEvent.type(screen.getByLabelText('Out point seconds'), '11') await userEvent.click(screen.getByRole('button', { name: 'Out point +1s' })) - await userEvent.click(screen.getByRole('button', { name: 'Out point +10ms' })) + await userEvent.click(screen.getByRole('button', { name: 'Out point +100ms' })) await userEvent.click(screen.getByRole('button', { name: 'Save as clip' })) - expect(create).toHaveBeenCalledWith('a1', { in_point: 1.5, out_point: 12.01 }) + expect(create).toHaveBeenCalledWith('a1', { in_point: 1.5, out_point: 12.1 }) }) it('stops a nudge at the end of the asset', async () => { @@ -259,17 +259,17 @@ describe('ClipEditor, deleting a clip', () => { vi.spyOn(clipsApi, 'list').mockResolvedValue([liveClip]) const setRange = vi .spyOn(clipsApi, 'setRange') - .mockResolvedValue({ ...liveClip, in_point: 4.99, out_point: 9 }) + .mockResolvedValue({ ...liveClip, in_point: 4.9, out_point: 9 }) renderEditor() await userEvent.click( await screen.findByRole('button', { name: "Edit this clip's times" }) ) // The new-clip fields are above; the row's form is the second pair. - await userEvent.click(screen.getAllByRole('button', { name: 'In point −10ms' })[1]) + await userEvent.click(screen.getAllByRole('button', { name: 'In point −100ms' })[1]) await userEvent.click(screen.getByRole('button', { name: 'Save times' })) - expect(setRange).toHaveBeenCalledWith(liveClip.id, { in_point: 4.99, out_point: 9 }) - expect(await screen.findByText('00:00:04:990 – 00:00:09:000')).toBeInTheDocument() + expect(setRange).toHaveBeenCalledWith(liveClip.id, { in_point: 4.9, out_point: 9 }) + expect(await screen.findByText('00:00:04:900 – 00:00:09:000')).toBeInTheDocument() }) }) diff --git a/frontend/src/components/TimecodeInput.tsx b/frontend/src/components/TimecodeInput.tsx index 567bdd6..ade955d 100644 --- a/frontend/src/components/TimecodeInput.tsx +++ b/frontend/src/components/TimecodeInput.tsx @@ -2,12 +2,12 @@ import { useEffect, useRef, useState } from 'react' import { Crosshair, Play } from 'lucide-react' import { fromTimecodeParts, toTimecodeParts, type TimecodeParts } from '@/utils/format' -/** The nudge buttons, smallest step innermost. Frames are 33–42 ms at common rates, - * so 10 ms is fine enough to land on one without a hundred presses. */ +/** The nudge buttons, smallest step innermost. 100 ms is a few frames at common rates: + * quick to walk with, and the milliseconds field is there for anything finer. */ const NUDGES = [ { delta: -1, label: '−1s' }, - { delta: -0.01, label: '−10ms' }, - { delta: 0.01, label: '+10ms' }, + { delta: -0.1, label: '−100ms' }, + { delta: 0.1, label: '+100ms' }, { delta: 1, label: '+1s' }, ] @@ -33,8 +33,8 @@ interface Props { setLabel?: string } -/** Rounded to the millisecond, the precision the fields show: without it, ten +10 ms - * nudges from 0 land on 0.09999999999999999 and the display drifts a millisecond. */ +/** Rounded to the millisecond, the precision the fields show: without it, three +100 ms + * nudges from 0 land on 0.30000000000000004, float noise that would be saved as is. */ function clamp(seconds: number, max: number | undefined): number { const bounded = Math.max(0, max === undefined ? seconds : Math.min(seconds, max)) return Math.round(bounded * 1000) / 1000