diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index 3b4c73d4..7d23ee7d 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -182,6 +182,7 @@ function App() {
const durationSafe = isDurationValid(duration) ? duration : 1;
const zoom = Math.floor(zoomUnrounded);
const zoomedDuration = isDurationValid(duration) ? duration / zoom : undefined;
+ const zoomWindowEndTime = useMemo(() => (zoomedDuration != null ? zoomWindowStartTime + zoomedDuration : undefined), [zoomedDuration, zoomWindowStartTime]);
useEffect(() => setDocumentTitle({ filePath, working: working?.text, progress }), [progress, filePath, working?.text]);
@@ -2526,6 +2527,8 @@ function App() {
inverseCutSegments={inverseCutSegments}
formatTimecode={formatTimecode}
formatTimeAndFrames={formatTimeAndFrames}
+ zoomWindowStartTime={zoomWindowStartTime}
+ zoomWindowEndTime={zoomWindowEndTime}
onZoomWindowStartTimeChange={setZoomWindowStartTime}
playing={playing}
isFileOpened={isFileOpened}
diff --git a/src/renderer/src/Timeline.tsx b/src/renderer/src/Timeline.tsx
index 0bf76c33..2e541611 100644
--- a/src/renderer/src/Timeline.tsx
+++ b/src/renderer/src/Timeline.tsx
@@ -1,4 +1,4 @@
-import { memo, useRef, useMemo, useCallback, useEffect, useState, MutableRefObject, CSSProperties, WheelEventHandler } from 'react';
+import { memo, useRef, useMemo, useCallback, useEffect, useState, MutableRefObject, CSSProperties, WheelEventHandler, MouseEventHandler } from 'react';
import { motion, useMotionValue, useSpring } from 'framer-motion';
import debounce from 'lodash/debounce';
import { useTranslation } from 'react-i18next';
@@ -48,7 +48,11 @@ const Waveform = memo(({ waveform, calculateTimelinePercent, durationSafe }: {
// eslint-disable-next-line react/display-name
const Waveforms = memo(({ calculateTimelinePercent, durationSafe, waveforms, zoom, height }: {
- calculateTimelinePercent: CalculateTimelinePercent, durationSafe: number, waveforms: RenderableWaveform[], zoom: number, height: number,
+ calculateTimelinePercent: CalculateTimelinePercent,
+ durationSafe: number,
+ waveforms: RenderableWaveform[],
+ zoom: number,
+ height: number,
}) => (
{waveforms.map((waveform) => (
@@ -93,6 +97,8 @@ function Timeline({
shouldShowWaveform,
shouldShowKeyframes,
thumbnails,
+ zoomWindowStartTime,
+ zoomWindowEndTime,
onZoomWindowStartTimeChange,
waveformEnabled,
showThumbnails,
@@ -121,6 +127,8 @@ function Timeline({
shouldShowWaveform: boolean,
shouldShowKeyframes: boolean,
thumbnails: Thumbnail[],
+ zoomWindowStartTime: number,
+ zoomWindowEndTime: number | undefined,
onZoomWindowStartTimeChange: (a: number) => void,
waveformEnabled: boolean,
showThumbnails: boolean,
@@ -147,14 +155,14 @@ function Timeline({
const isZoomed = zoom > 1;
+ const keyFramesInZoomWindow = useMemo(() => (zoomWindowEndTime == null ? [] : neighbouringKeyFrames.filter((f) => f.time >= zoomWindowStartTime && f.time <= zoomWindowEndTime)), [neighbouringKeyFrames, zoomWindowEndTime, zoomWindowStartTime]);
+
// Don't show keyframes if too packed together (at current zoom)
// See https://github.com/mifi/lossless-cut/issues/259
- // todo
- // const areKeyframesTooClose = keyframes.length > zoom * 200;
- const areKeyframesTooClose = false;
+ const areKeyframesTooClose = keyFramesInZoomWindow.length > zoom * 200;
- const calculateTimelinePos = useCallback((time) => (time !== undefined ? Math.min(time / durationSafe, 1) : undefined), [durationSafe]);
- const calculateTimelinePercent = useCallback((time) => {
+ const calculateTimelinePos = useCallback((time: number | undefined) => (time !== undefined ? Math.min(time / durationSafe, 1) : undefined), [durationSafe]);
+ const calculateTimelinePercent = useCallback((time: number | undefined) => {
const pos = calculateTimelinePos(time);
return pos !== undefined ? `${pos * 100}%` : undefined;
}, [calculateTimelinePos]);
@@ -228,7 +236,7 @@ function Timeline({
useEffect(() => {
- const cancelWheel = (event) => event.preventDefault();
+ const cancelWheel = (event: WheelEvent) => event.preventDefault();
const scroller = timelineScrollerRef.current;
invariant(scroller != null);
@@ -253,7 +261,7 @@ function Timeline({
/ (timelineScrollerRef.current.offsetWidth * zoom)) * duration));
}, [duration, seekAbs, zoomed, zoom, zoomWindowStartTime, onZoomWindowStartTimeChange]); */
- const getMouseTimelinePos = useCallback((e) => {
+ const getMouseTimelinePos = useCallback((e: MouseEvent) => {
const target = timelineWrapperRef.current;
invariant(target != null);
const rect = target.getBoundingClientRect();
@@ -261,22 +269,22 @@ function Timeline({
return (relX / target.offsetWidth) * durationSafe;
}, [durationSafe]);
- const mouseDownRef = useRef();
+ const mouseDownRef = useRef
();
- const handleScrub = useCallback((e) => seekAbs((getMouseTimelinePos(e))), [seekAbs, getMouseTimelinePos]);
+ const handleScrub = useCallback((e: MouseEvent) => seekAbs((getMouseTimelinePos(e))), [seekAbs, getMouseTimelinePos]);
useEffect(() => {
setHoveringTime(undefined);
}, [relevantTime]);
- const onMouseDown = useCallback((e) => {
+ const onMouseDown = useCallback>((e) => {
if (e.nativeEvent.buttons !== 1) return; // not primary button
handleScrub(e.nativeEvent);
mouseDownRef.current = e.target;
- function onMouseMove(e2) {
+ function onMouseMove(e2: MouseEvent) {
if (mouseDownRef.current == null) return;
seekAbs(getMouseTimelinePos(e2));
}
@@ -294,7 +302,7 @@ function Timeline({
window.addEventListener('mousemove', onMouseMove);
}, [getMouseTimelinePos, handleScrub, seekAbs]);
- const onMouseMove = useCallback((e) => {
+ const onMouseMove = useCallback>((e) => {
if (!mouseDownRef.current) { // no button pressed
setHoveringTime(getMouseTimelinePos(e.nativeEvent));
}
@@ -389,7 +397,7 @@ function Timeline({
/>
))}
- {shouldShowKeyframes && !areKeyframesTooClose && neighbouringKeyFrames.map((f) => (
+ {shouldShowKeyframes && !areKeyframesTooClose && keyFramesInZoomWindow.map((f) => (
))}
diff --git a/src/renderer/src/TimelineSeg.tsx b/src/renderer/src/TimelineSeg.tsx
index 5cb9d0f5..43c9370a 100644
--- a/src/renderer/src/TimelineSeg.tsx
+++ b/src/renderer/src/TimelineSeg.tsx
@@ -11,7 +11,14 @@ import { ApparentCutSegment, FormatTimecode } from './types';
function TimelineSeg({
seg, duration, isActive, segNum, onSegClick, invertCutSegments, formatTimecode, selected,
} : {
- seg: ApparentCutSegment, duration: number, isActive: boolean, segNum: number, onSegClick: (a: number) => void, invertCutSegments: boolean, formatTimecode: FormatTimecode, selected: boolean,
+ seg: ApparentCutSegment,
+ duration: number,
+ isActive: boolean,
+ segNum: number,
+ onSegClick: (a: number) => void,
+ invertCutSegments: boolean,
+ formatTimecode: FormatTimecode,
+ selected: boolean,
}) {
const { darkMode } = useUserSettings();
const { getSegColor } = useSegColors();
diff --git a/src/renderer/src/components/Switch.tsx b/src/renderer/src/components/Switch.tsx
index f256be13..1d05690f 100644
--- a/src/renderer/src/components/Switch.tsx
+++ b/src/renderer/src/components/Switch.tsx
@@ -1,12 +1,11 @@
-import { CSSProperties } from 'react';
+import { RefAttributes } from 'react';
import * as RadixSwitch from '@radix-ui/react-switch';
import classes from './Switch.module.css';
-const Switch = ({ checked, disabled, onCheckedChange, title, style }: {
- checked: boolean, disabled?: boolean, onCheckedChange: (v: boolean) => void, title?: string, style?: CSSProperties,
-}) => (
-
+const Switch = (props: RadixSwitch.SwitchProps & RefAttributes) => (
+ // eslint-disable-next-line react/jsx-props-no-spreading
+
);
diff --git a/src/renderer/src/hooks/useKeyframes.ts b/src/renderer/src/hooks/useKeyframes.ts
index 62369339..8e0819bb 100644
--- a/src/renderer/src/hooks/useKeyframes.ts
+++ b/src/renderer/src/hooks/useKeyframes.ts
@@ -8,9 +8,14 @@ import { FFprobeStream } from '../../../../ffprobe';
const maxKeyframes = 1000;
// const maxKeyframes = 100;
-export default ({ keyframesEnabled, filePath, commandedTime, videoStream, detectedFps, ffmpegExtractWindow }: {
- keyframesEnabled: boolean, filePath: string | undefined, commandedTime: number, videoStream: FFprobeStream | undefined, detectedFps: number | undefined, ffmpegExtractWindow: number,
-}) => {
+function useKeyframes({ keyframesEnabled, filePath, commandedTime, videoStream, detectedFps, ffmpegExtractWindow }: {
+ keyframesEnabled: boolean,
+ filePath: string | undefined,
+ commandedTime: number,
+ videoStream: FFprobeStream | undefined,
+ detectedFps: number | undefined,
+ ffmpegExtractWindow: number,
+}) {
const readingKeyframesPromise = useRef>();
const [neighbouringKeyFramesMap, setNeighbouringKeyFrames] = useState>({});
const neighbouringKeyFrames = useMemo(() => Object.values(neighbouringKeyFramesMap), [neighbouringKeyFramesMap]);
@@ -61,4 +66,6 @@ export default ({ keyframesEnabled, filePath, commandedTime, videoStream, detect
return {
neighbouringKeyFrames, findNearestKeyFrameTime,
};
-};
+}
+
+export default useKeyframes;