chore: video-editor update with new prettier settings

This commit is contained in:
Yiannis Christodoulou 2025-07-27 21:25:44 +03:00
parent eaf87e20d8
commit 074638e237
25 changed files with 12707 additions and 10118 deletions

View File

@ -1,5 +1,5 @@
import { formatTime, formatLongTime } from "@/lib/timeUtils";
import "../styles/ClipSegments.css";
import { formatTime, formatLongTime } from '@/lib/timeUtils';
import '../styles/ClipSegments.css';
export interface Segment {
id: number;
@ -20,8 +20,8 @@ const ClipSegments = ({ segments }: ClipSegmentsProps) => {
// Handle delete segment click
const handleDeleteSegment = (segmentId: number) => {
// Create and dispatch the delete event
const deleteEvent = new CustomEvent("delete-segment", {
detail: { segmentId }
const deleteEvent = new CustomEvent('delete-segment', {
detail: { segmentId },
});
document.dispatchEvent(deleteEvent);
};
@ -74,9 +74,7 @@ const ClipSegments = ({ segments }: ClipSegmentsProps) => {
))}
{sortedSegments.length === 0 && (
<div className="empty-message">
No segments created yet. Use the split button to create segments.
</div>
<div className="empty-message">No segments created yet. Use the split button to create segments.</div>
)}
</div>
);

View File

@ -1,5 +1,5 @@
import "../styles/EditingTools.css";
import { useEffect, useState } from "react";
import '../styles/EditingTools.css';
import { useEffect, useState } from 'react';
interface EditingToolsProps {
onSplit: () => void;
@ -24,7 +24,7 @@ const EditingTools = ({
canUndo,
canRedo,
isPlaying = false,
isPlayingSegments = false
isPlayingSegments = false,
}: EditingToolsProps) => {
const [isSmallScreen, setIsSmallScreen] = useState(false);
@ -34,15 +34,15 @@ const EditingTools = ({
};
checkScreenSize();
window.addEventListener("resize", checkScreenSize);
return () => window.removeEventListener("resize", checkScreenSize);
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
// Handle play button click with iOS fix
const handlePlay = () => {
// Ensure lastSeekedPosition is used when play is clicked
if (typeof window !== "undefined") {
console.log("Play button clicked, current lastSeekedPosition:", window.lastSeekedPosition);
if (typeof window !== 'undefined') {
console.log('Play button clicked, current lastSeekedPosition:', window.lastSeekedPosition);
}
// Call the original handler
@ -59,9 +59,9 @@ const EditingTools = ({
className={`button segments-button`}
onClick={onPlaySegments}
data-tooltip={
isPlayingSegments ? "Stop segments playback" : "Play segments in one continuous flow"
isPlayingSegments ? 'Stop segments playback' : 'Play segments in one continuous flow'
}
style={{ fontSize: "0.875rem" }}
style={{ fontSize: '0.875rem' }}
>
{isPlayingSegments ? (
<>
@ -133,10 +133,10 @@ const EditingTools = ({
{/* Standard Play button (only shown when not in segments playback on small screens) */}
{(!isPlayingSegments || !isSmallScreen) && (
<button
className={`button play-button ${isPlayingSegments ? "greyed-out" : ""}`}
className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
onClick={handlePlay}
data-tooltip={isPlaying ? "Pause video" : "Play full video"}
style={{ fontSize: "0.875rem" }}
data-tooltip={isPlaying ? 'Pause video' : 'Play full video'}
style={{ fontSize: '0.875rem' }}
disabled={isPlayingSegments}
>
{isPlaying && !isPlayingSegments ? (
@ -208,7 +208,7 @@ const EditingTools = ({
<button
className="button"
aria-label="Undo"
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Undo last action"}
data-tooltip={isPlayingSegments ? 'Disabled during preview' : 'Undo last action'}
disabled={!canUndo || isPlayingSegments}
onClick={onUndo}
>
@ -229,7 +229,7 @@ const EditingTools = ({
<button
className="button"
aria-label="Redo"
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Redo last undone action"}
data-tooltip={isPlayingSegments ? 'Disabled during preview' : 'Redo last undone action'}
disabled={!canRedo || isPlayingSegments}
onClick={onRedo}
>
@ -251,7 +251,7 @@ const EditingTools = ({
<button
className="button"
onClick={onReset}
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Reset to full video"}
data-tooltip={isPlayingSegments ? 'Disabled during preview' : 'Reset to full video'}
disabled={isPlayingSegments}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import "../styles/IOSPlayPrompt.css";
import React, { useState, useEffect } from 'react';
import '../styles/IOSPlayPrompt.css';
interface MobilePlayPromptProps {
videoRef: React.RefObject<HTMLVideoElement>;
@ -33,9 +33,9 @@ const MobilePlayPrompt: React.FC<MobilePlayPromptProps> = ({ videoRef, onPlay })
setIsVisible(false);
};
video.addEventListener("play", handlePlay);
video.addEventListener('play', handlePlay);
return () => {
video.removeEventListener("play", handlePlay);
video.removeEventListener('play', handlePlay);
};
}, [videoRef]);

View File

@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from "react";
import { formatTime } from "@/lib/timeUtils";
import "../styles/IOSVideoPlayer.css";
import { useEffect, useState, useRef } from 'react';
import { formatTime } from '@/lib/timeUtils';
import '../styles/IOSVideoPlayer.css';
interface IOSVideoPlayerProps {
videoRef: React.RefObject<HTMLVideoElement>;
@ -9,7 +9,7 @@ interface IOSVideoPlayerProps {
}
const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps) => {
const [videoUrl, setVideoUrl] = useState<string>("");
const [videoUrl, setVideoUrl] = useState<string>('');
const [iosVideoRef, setIosVideoRef] = useState<HTMLVideoElement | null>(null);
// Refs for hold-to-continue functionality
@ -26,14 +26,14 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
// Get the video source URL from the main player
useEffect(() => {
if (videoRef.current && videoRef.current.querySelector("source")) {
const source = videoRef.current.querySelector("source") as HTMLSourceElement;
if (videoRef.current && videoRef.current.querySelector('source')) {
const source = videoRef.current.querySelector('source') as HTMLSourceElement;
if (source && source.src) {
setVideoUrl(source.src);
}
} else {
// Fallback to sample video if needed
setVideoUrl("/videos/sample-video-10m.mp4");
setVideoUrl('/videos/sample-video.mp4');
}
}, [videoRef]);

View File

@ -1,5 +1,5 @@
import React, { useEffect } from "react";
import "../styles/Modal.css";
import React, { useEffect } from 'react';
import '../styles/Modal.css';
interface ModalProps {
isOpen: boolean;
@ -13,21 +13,21 @@ const Modal: React.FC<ModalProps> = ({ isOpen, onClose, title, children, actions
// Close modal when Escape key is pressed
useEffect(() => {
const handleEscapeKey = (event: KeyboardEvent) => {
if (event.key === "Escape" && isOpen) {
if (event.key === 'Escape' && isOpen) {
onClose();
}
};
document.addEventListener("keydown", handleEscapeKey);
document.addEventListener('keydown', handleEscapeKey);
// Disable body scrolling when modal is open
if (isOpen) {
document.body.style.overflow = "hidden";
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener("keydown", handleEscapeKey);
document.body.style.overflow = "";
document.removeEventListener('keydown', handleEscapeKey);
document.body.style.overflow = '';
};
}, [isOpen, onClose]);

View File

@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState } from "react";
import { formatTime, formatDetailedTime } from "@/lib/timeUtils";
import logger from "../lib/logger";
import "../styles/VideoPlayer.css";
import React, { useRef, useEffect, useState } from 'react';
import { formatTime, formatDetailedTime } from '@/lib/timeUtils';
import logger from '../lib/logger';
import '../styles/VideoPlayer.css';
interface VideoPlayerProps {
videoRef: React.RefObject<HTMLVideoElement>;
@ -22,7 +22,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
isMuted = false,
onPlayPause,
onSeek,
onToggleMute
onToggleMute,
}) => {
const progressRef = useRef<HTMLDivElement>(null);
const [isIOS, setIsIOS] = useState(false);
@ -30,12 +30,13 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const [lastPosition, setLastPosition] = useState<number | null>(null);
const [isDraggingProgress, setIsDraggingProgress] = useState(false);
const isDraggingProgressRef = useRef(false);
const [tooltipPosition, setTooltipPosition] = useState({ x: 0 });
const [tooltipPosition, setTooltipPosition] = useState({
x: 0,
});
const [tooltipTime, setTooltipTime] = useState(0);
const sampleVideoUrl =
(typeof window !== "undefined" && (window as any).MEDIA_DATA?.videoUrl) ||
"/videos/sample-video-10m.mp4";
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.videoUrl) || '/videos/sample-video.mp4';
// Detect iOS device
useEffect(() => {
@ -47,8 +48,8 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
setIsIOS(checkIOS());
// Check if video was previously initialized
if (typeof window !== "undefined") {
const wasInitialized = localStorage.getItem("video_initialized") === "true";
if (typeof window !== 'undefined') {
const wasInitialized = localStorage.getItem('video_initialized') === 'true';
setHasInitialized(wasInitialized);
}
}, []);
@ -57,8 +58,8 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
useEffect(() => {
if (isPlaying && !hasInitialized) {
setHasInitialized(true);
if (typeof window !== "undefined") {
localStorage.setItem("video_initialized", "true");
if (typeof window !== 'undefined') {
localStorage.setItem('video_initialized', 'true');
}
}
}, [isPlaying, hasInitialized]);
@ -70,15 +71,15 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
// These attributes need to be set directly on the DOM element
// for iOS Safari to respect inline playback
video.setAttribute("playsinline", "true");
video.setAttribute("webkit-playsinline", "true");
video.setAttribute("x-webkit-airplay", "allow");
video.setAttribute('playsinline', 'true');
video.setAttribute('webkit-playsinline', 'true');
video.setAttribute('x-webkit-airplay', 'allow');
// Store the last known good position for iOS
const handleTimeUpdate = () => {
if (!isDraggingProgressRef.current) {
setLastPosition(video.currentTime);
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
window.lastSeekedPosition = video.currentTime;
}
}
@ -86,25 +87,25 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
// Handle iOS-specific play/pause state
const handlePlay = () => {
logger.debug("Video play event fired");
logger.debug('Video play event fired');
if (isIOS) {
setHasInitialized(true);
localStorage.setItem("video_initialized", "true");
localStorage.setItem('video_initialized', 'true');
}
};
const handlePause = () => {
logger.debug("Video pause event fired");
logger.debug('Video pause event fired');
};
video.addEventListener("timeupdate", handleTimeUpdate);
video.addEventListener("play", handlePlay);
video.addEventListener("pause", handlePause);
video.addEventListener('timeupdate', handleTimeUpdate);
video.addEventListener('play', handlePlay);
video.addEventListener('pause', handlePause);
return () => {
video.removeEventListener("timeupdate", handleTimeUpdate);
video.removeEventListener("play", handlePlay);
video.removeEventListener("pause", handlePause);
video.removeEventListener('timeupdate', handleTimeUpdate);
video.removeEventListener('play', handlePlay);
video.removeEventListener('pause', handlePause);
};
}, [videoRef, isIOS, isDraggingProgressRef]);
@ -150,12 +151,12 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const handleMouseUp = () => {
setIsDraggingProgress(false);
isDraggingProgressRef.current = false;
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
// Handle progress dragging for both mouse and touch events
@ -167,14 +168,16 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const seekTime = duration * clickPosition;
// Update tooltip position and time
setTooltipPosition({ x: e.clientX });
setTooltipPosition({
x: e.clientX,
});
setTooltipTime(seekTime);
// Store position locally for iOS Safari - critical for timeline seeking
setLastPosition(seekTime);
// Also store globally for integration with other components
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
(window as any).lastSeekedPosition = seekTime;
}
@ -202,14 +205,16 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const handleTouchEnd = () => {
setIsDraggingProgress(false);
isDraggingProgressRef.current = false;
document.removeEventListener("touchmove", handleTouchMove);
document.removeEventListener("touchend", handleTouchEnd);
document.removeEventListener("touchcancel", handleTouchEnd);
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleTouchEnd);
document.removeEventListener('touchcancel', handleTouchEnd);
};
document.addEventListener("touchmove", handleTouchMove, { passive: false });
document.addEventListener("touchend", handleTouchEnd);
document.addEventListener("touchcancel", handleTouchEnd);
document.addEventListener('touchmove', handleTouchMove, {
passive: false,
});
document.addEventListener('touchend', handleTouchEnd);
document.addEventListener('touchcancel', handleTouchEnd);
};
// Handle touch dragging on progress bar
@ -217,7 +222,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
if (!progressRef.current) return;
// Get the touch coordinates
const touch = "touches" in e ? e.touches[0] : null;
const touch = 'touches' in e ? e.touches[0] : null;
if (!touch) return;
e.preventDefault(); // Prevent scrolling while dragging
@ -227,14 +232,16 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const seekTime = duration * touchPosition;
// Update tooltip position and time
setTooltipPosition({ x: touch.clientX });
setTooltipPosition({
x: touch.clientX,
});
setTooltipTime(seekTime);
// Store position for iOS Safari
setLastPosition(seekTime);
// Also store globally for integration with other components
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
(window as any).lastSeekedPosition = seekTime;
}
@ -255,7 +262,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
setLastPosition(seekTime);
// Also store globally for integration with other components
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
(window as any).lastSeekedPosition = seekTime;
}
@ -283,7 +290,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
if (video.paused) {
// For iOS Safari: Before playing, explicitly seek to the remembered position
if (isIOS && lastPosition !== null && lastPosition > 0) {
logger.debug("iOS: Explicitly setting position before play:", lastPosition);
logger.debug('iOS: Explicitly setting position before play:', lastPosition);
// First, seek to the position
video.currentTime = lastPosition;
@ -296,13 +303,13 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
.play()
.then(() => {
logger.debug(
"iOS: Play started successfully at position:",
'iOS: Play started successfully at position:',
videoRef.current?.currentTime
);
onPlayPause(); // Update parent state after successful play
})
.catch((err) => {
console.error("iOS: Error playing video:", err);
console.error('iOS: Error playing video:', err);
});
}
}, 50);
@ -311,11 +318,11 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
video
.play()
.then(() => {
logger.debug("Normal: Play started successfully");
logger.debug('Normal: Play started successfully');
onPlayPause(); // Update parent state after successful play
})
.catch((err) => {
console.error("Error playing video:", err);
console.error('Error playing video:', err);
});
}
} else {
@ -350,7 +357,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
)}
{/* Play/Pause Indicator (shows based on current state) */}
<div className={`play-pause-indicator ${isPlaying ? "pause-icon" : "play-icon"}`}></div>
<div className={`play-pause-indicator ${isPlaying ? 'pause-icon' : 'play-icon'}`}></div>
{/* Video Controls Overlay */}
<div className="video-controls">
@ -363,13 +370,23 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
{/* Progress Bar with enhanced dragging */}
<div
ref={progressRef}
className={`video-progress ${isDraggingProgress ? "dragging" : ""}`}
className={`video-progress ${isDraggingProgress ? 'dragging' : ''}`}
onClick={handleProgressClick}
onMouseDown={handleProgressDragStart}
onTouchStart={handleProgressTouchStart}
>
<div className="video-progress-fill" style={{ width: `${progressPercentage}%` }}></div>
<div className="video-scrubber" style={{ left: `${progressPercentage}%` }}></div>
<div
className="video-progress-fill"
style={{
width: `${progressPercentage}%`,
}}
></div>
<div
className="video-scrubber"
style={{
left: `${progressPercentage}%`,
}}
></div>
{/* Floating time tooltip when dragging */}
{isDraggingProgress && (
@ -377,7 +394,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
className="video-time-tooltip"
style={{
left: `${tooltipPosition.x}px`,
transform: "translateX(-50%)"
transform: 'translateX(-50%)',
}}
>
{formatDetailedTime(tooltipTime)}
@ -391,9 +408,9 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
{onToggleMute && (
<button
className="mute-button"
aria-label={isMuted ? "Unmute" : "Mute"}
aria-label={isMuted ? 'Unmute' : 'Mute'}
onClick={onToggleMute}
data-tooltip={isMuted ? "Unmute" : "Mute"}
data-tooltip={isMuted ? 'Unmute' : 'Mute'}
>
{isMuted ? (
<svg

View File

@ -1,8 +1,8 @@
import { useState, useRef, useEffect } from "react";
import { generateThumbnail } from "@/lib/videoUtils";
import { formatDetailedTime } from "@/lib/timeUtils";
import logger from "@/lib/logger";
import type { Segment } from "@/components/ClipSegments";
import { useState, useRef, useEffect } from 'react';
import { generateThumbnail } from '@/lib/videoUtils';
import { formatDetailedTime } from '@/lib/timeUtils';
import logger from '@/lib/logger';
import type { Segment } from '@/components/ClipSegments';
// Represents a state of the editor for undo/redo
interface EditorState {
@ -46,21 +46,18 @@ const useVideoTrimmer = () => {
useEffect(() => {
if (history.length > 0) {
// For debugging - moved to console.debug
if (process.env.NODE_ENV === "development") {
console.debug(
`History state updated: ${history.length} entries, position: ${historyPosition}`
);
if (process.env.NODE_ENV === 'development') {
console.debug(`History state updated: ${history.length} entries, position: ${historyPosition}`);
// Log actions in history to help debug undo/redo
const actions = history.map(
(state, idx) =>
`${idx}: ${state.action || "unknown"} (segments: ${state.clipSegments.length})`
(state, idx) => `${idx}: ${state.action || 'unknown'} (segments: ${state.clipSegments.length})`
);
console.debug("History actions:", actions);
console.debug('History actions:', actions);
}
// If there's at least one history entry and it wasn't a save operation, mark as having unsaved changes
const lastAction = history[historyPosition]?.action || "";
if (lastAction !== "save" && lastAction !== "save_copy" && lastAction !== "save_segments") {
const lastAction = history[historyPosition]?.action || '';
if (lastAction !== 'save' && lastAction !== 'save_copy' && lastAction !== 'save_segments') {
setHasUnsavedChanges(true);
}
}
@ -72,7 +69,7 @@ const useVideoTrimmer = () => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (hasUnsavedChanges) {
// Standard way of showing a confirmation dialog before leaving
const message = "Your edits will get lost if you leave the page. Do you want to continue?";
const message = 'Your edits will get lost if you leave the page. Do you want to continue?';
e.preventDefault();
e.returnValue = message; // Chrome requires returnValue to be set
return message; // For other browsers
@ -80,11 +77,11 @@ const useVideoTrimmer = () => {
};
// Add event listener
window.addEventListener("beforeunload", handleBeforeUnload);
window.addEventListener('beforeunload', handleBeforeUnload);
// Clean up
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [hasUnsavedChanges]);
@ -105,10 +102,10 @@ const useVideoTrimmer = () => {
// Create an initial segment that spans the entire video
const initialSegment: Segment = {
id: 1,
name: "segment",
name: 'segment',
startTime: 0,
endTime: video.duration,
thumbnail: segmentThumbnail
thumbnail: segmentThumbnail,
};
// Initialize history state with the full-length segment
@ -116,7 +113,7 @@ const useVideoTrimmer = () => {
trimStart: 0,
trimEnd: video.duration,
splitPoints: [],
clipSegments: [initialSegment]
clipSegments: [initialSegment],
};
setHistory([initialState]);
@ -159,19 +156,19 @@ const useVideoTrimmer = () => {
};
// Add event listeners
video.addEventListener("loadedmetadata", handleLoadedMetadata);
video.addEventListener("timeupdate", handleTimeUpdate);
video.addEventListener("play", handlePlay);
video.addEventListener("pause", handlePause);
video.addEventListener("ended", handleEnded);
video.addEventListener('loadedmetadata', handleLoadedMetadata);
video.addEventListener('timeupdate', handleTimeUpdate);
video.addEventListener('play', handlePlay);
video.addEventListener('pause', handlePause);
video.addEventListener('ended', handleEnded);
return () => {
// Remove event listeners
video.removeEventListener("loadedmetadata", handleLoadedMetadata);
video.removeEventListener("timeupdate", handleTimeUpdate);
video.removeEventListener("play", handlePlay);
video.removeEventListener("pause", handlePause);
video.removeEventListener("ended", handleEnded);
video.removeEventListener('loadedmetadata', handleLoadedMetadata);
video.removeEventListener('timeupdate', handleTimeUpdate);
video.removeEventListener('play', handlePlay);
video.removeEventListener('pause', handlePause);
video.removeEventListener('ended', handleEnded);
};
}, []);
@ -184,7 +181,7 @@ const useVideoTrimmer = () => {
video.pause();
} else {
// iOS Safari fix: Use the last seeked position if available
if (!isPlaying && typeof window !== "undefined" && window.lastSeekedPosition > 0) {
if (!isPlaying && typeof window !== 'undefined' && window.lastSeekedPosition > 0) {
// Only apply this if the video is not at the same position already
// This avoids unnecessary seeking which might cause playback issues
if (Math.abs(video.currentTime - window.lastSeekedPosition) > 0.1) {
@ -201,12 +198,12 @@ const useVideoTrimmer = () => {
.then(() => {
// Play started successfully
// Reset the last seeked position after successfully starting playback
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
window.lastSeekedPosition = 0;
}
})
.catch((err) => {
console.error("Error starting playback:", err);
console.error('Error starting playback:', err);
setIsPlaying(false); // Reset state if play failed
});
}
@ -226,7 +223,7 @@ const useVideoTrimmer = () => {
// Store the position in a global state accessible to iOS Safari
// This ensures when play is pressed later, it remembers the position
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
window.lastSeekedPosition = time;
}
@ -239,7 +236,7 @@ const useVideoTrimmer = () => {
setIsPlaying(true); // Update state to reflect we're playing
})
.catch((err) => {
console.error("Error resuming playback:", err);
console.error('Error resuming playback:', err);
setIsPlaying(false);
});
}
@ -254,7 +251,7 @@ const useVideoTrimmer = () => {
trimEnd,
splitPoints: [...splitPoints],
clipSegments: JSON.parse(JSON.stringify(clipSegments)), // Deep clone to avoid reference issues
action: action || "manual_save" // Track the action that triggered this save
action: action || 'manual_save', // Track the action that triggered this save
};
// Check if state is significantly different from last saved state
@ -339,16 +336,16 @@ const useVideoTrimmer = () => {
if (recordHistory) {
// Use a small timeout to ensure the state is updated
setTimeout(() => {
saveState(action || (isStart ? "adjust_trim_start" : "adjust_trim_end"));
saveState(action || (isStart ? 'adjust_trim_start' : 'adjust_trim_end'));
}, 10);
}
}
};
document.addEventListener("update-trim", handleTrimUpdate as EventListener);
document.addEventListener('update-trim', handleTrimUpdate as EventListener);
return () => {
document.removeEventListener("update-trim", handleTrimUpdate as EventListener);
document.removeEventListener('update-trim', handleTrimUpdate as EventListener);
};
}, []);
@ -360,11 +357,11 @@ const useVideoTrimmer = () => {
// Default to true to ensure all segment changes are recorded
const isSignificantChange = e.detail.recordHistory !== false;
// Get the action type if provided
const actionType = e.detail.action || "update_segments";
const actionType = e.detail.action || 'update_segments';
// Log the update details
logger.debug(
`Updating segments with action: ${actionType}, recordHistory: ${isSignificantChange ? "true" : "false"}`
`Updating segments with action: ${actionType}, recordHistory: ${isSignificantChange ? 'true' : 'false'}`
);
// Update segment state immediately for UI feedback
@ -384,7 +381,7 @@ const useVideoTrimmer = () => {
trimEnd,
splitPoints: [...splitPoints],
clipSegments: segmentsClone,
action: actionType // Store the action type in the state
action: actionType, // Store the action type in the state
};
// Get the current history position to ensure we're using the latest value
@ -405,16 +402,12 @@ const useVideoTrimmer = () => {
// Ensure the historyPosition is updated to the correct position
setHistoryPosition((prev) => {
const newPosition = prev + 1;
logger.debug(
`Saved state with action: ${actionType} to history position ${newPosition}`
);
logger.debug(`Saved state with action: ${actionType} to history position ${newPosition}`);
return newPosition;
});
}, 20); // Slightly increased delay to ensure state updates are complete
} else {
logger.debug(
`Skipped saving state to history for action: ${actionType} (recordHistory=false)`
);
logger.debug(`Skipped saving state to history for action: ${actionType} (recordHistory=false)`);
}
}
};
@ -423,8 +416,8 @@ const useVideoTrimmer = () => {
const customEvent = e as CustomEvent;
if (
customEvent.detail &&
typeof customEvent.detail.time === "number" &&
typeof customEvent.detail.segmentId === "number"
typeof customEvent.detail.time === 'number' &&
typeof customEvent.detail.segmentId === 'number'
) {
// Get the time and segment ID from the event
const timeToSplit = customEvent.detail.time;
@ -457,7 +450,7 @@ const useVideoTrimmer = () => {
name: `${segmentToSplit.name}-A`,
startTime: segmentToSplit.startTime,
endTime: timeToSplit,
thumbnail: "" // Empty placeholder - we'll use dynamic colors instead
thumbnail: '', // Empty placeholder - we'll use dynamic colors instead
};
// Create second half of the split segment - no thumbnail needed
@ -466,7 +459,7 @@ const useVideoTrimmer = () => {
name: `${segmentToSplit.name}-B`,
startTime: timeToSplit,
endTime: segmentToSplit.endTime,
thumbnail: "" // Empty placeholder - we'll use dynamic colors instead
thumbnail: '', // Empty placeholder - we'll use dynamic colors instead
};
// Add the new segments
@ -477,14 +470,14 @@ const useVideoTrimmer = () => {
// Update state
setClipSegments(newSegments);
saveState("split_segment");
saveState('split_segment');
}
};
// Handle delete segment event
const handleDeleteSegment = async (e: Event) => {
const customEvent = e as CustomEvent;
if (customEvent.detail && typeof customEvent.detail.segmentId === "number") {
if (customEvent.detail && typeof customEvent.detail.segmentId === 'number') {
const segmentId = customEvent.detail.segmentId;
// Find and remove the segment
@ -497,10 +490,10 @@ const useVideoTrimmer = () => {
// No need to generate a thumbnail - we'll use dynamic colors
const defaultSegment: Segment = {
id: Date.now(),
name: "segment",
name: 'segment',
startTime: 0,
endTime: videoRef.current.duration,
thumbnail: "" // Empty placeholder - we'll use dynamic colors instead
thumbnail: '', // Empty placeholder - we'll use dynamic colors instead
};
// Reset the trim points as well
@ -512,32 +505,32 @@ const useVideoTrimmer = () => {
// Just update the segments normally
setClipSegments(newSegments);
}
saveState("delete_segment");
saveState('delete_segment');
}
}
};
document.addEventListener("update-segments", handleUpdateSegments as EventListener);
document.addEventListener("split-segment", handleSplitSegment as EventListener);
document.addEventListener("delete-segment", handleDeleteSegment as EventListener);
document.addEventListener('update-segments', handleUpdateSegments as EventListener);
document.addEventListener('split-segment', handleSplitSegment as EventListener);
document.addEventListener('delete-segment', handleDeleteSegment as EventListener);
return () => {
document.removeEventListener("update-segments", handleUpdateSegments as EventListener);
document.removeEventListener("split-segment", handleSplitSegment as EventListener);
document.removeEventListener("delete-segment", handleDeleteSegment as EventListener);
document.removeEventListener('update-segments', handleUpdateSegments as EventListener);
document.removeEventListener('split-segment', handleSplitSegment as EventListener);
document.removeEventListener('delete-segment', handleDeleteSegment as EventListener);
};
}, [clipSegments, duration]);
// Handle trim start change
const handleTrimStartChange = (time: number) => {
setTrimStart(time);
saveState("adjust_trim_start");
saveState('adjust_trim_start');
};
// Handle trim end change
const handleTrimEndChange = (time: number) => {
setTrimEnd(time);
saveState("adjust_trim_end");
saveState('adjust_trim_end');
};
// Handle split at current position
@ -563,7 +556,7 @@ const useVideoTrimmer = () => {
name: `Segment ${i + 1}`,
startTime,
endTime,
thumbnail: "" // Empty placeholder - we'll use dynamic colors instead
thumbnail: '', // Empty placeholder - we'll use dynamic colors instead
});
startTime = endTime;
@ -571,7 +564,7 @@ const useVideoTrimmer = () => {
}
setClipSegments(newSegments);
saveState("create_split_points");
saveState('create_split_points');
}
};
@ -587,14 +580,14 @@ const useVideoTrimmer = () => {
// No need to generate thumbnails - we'll use dynamic colors
const defaultSegment: Segment = {
id: Date.now(),
name: "segment",
name: 'segment',
startTime: 0,
endTime: duration,
thumbnail: "" // Empty placeholder - we'll use dynamic colors instead
thumbnail: '', // Empty placeholder - we'll use dynamic colors instead
};
setClipSegments([defaultSegment]);
saveState("reset_all");
saveState('reset_all');
};
// Handle undo
@ -607,7 +600,7 @@ const useVideoTrimmer = () => {
// Log segment details to help debug
logger.debug(
"Segment details after undo:",
'Segment details after undo:',
previousState.clipSegments.map(
(seg) =>
`ID: ${seg.id}, Time: ${formatDetailedTime(seg.startTime)} - ${formatDetailedTime(seg.endTime)}`
@ -621,7 +614,7 @@ const useVideoTrimmer = () => {
setClipSegments(JSON.parse(JSON.stringify(previousState.clipSegments)));
setHistoryPosition(historyPosition - 1);
} else {
logger.debug("Cannot undo: at earliest history position");
logger.debug('Cannot undo: at earliest history position');
}
};
@ -635,7 +628,7 @@ const useVideoTrimmer = () => {
// Log segment details to help debug
logger.debug(
"Segment details after redo:",
'Segment details after redo:',
nextState.clipSegments.map(
(seg) =>
`ID: ${seg.id}, Time: ${formatDetailedTime(seg.startTime)} - ${formatDetailedTime(seg.endTime)}`
@ -649,7 +642,7 @@ const useVideoTrimmer = () => {
setClipSegments(JSON.parse(JSON.stringify(nextState.clipSegments)));
setHistoryPosition(historyPosition + 1);
} else {
logger.debug("Cannot redo: at latest history position");
logger.debug('Cannot redo: at latest history position');
}
};
@ -669,10 +662,10 @@ const useVideoTrimmer = () => {
setIsPlaying(false);
} else {
// iOS Safari fix: Check for lastSeekedPosition
if (typeof window !== "undefined" && window.lastSeekedPosition > 0) {
if (typeof window !== 'undefined' && window.lastSeekedPosition > 0) {
// Only seek if the position is significantly different
if (Math.abs(video.currentTime - window.lastSeekedPosition) > 0.1) {
console.log("handlePlay: Using lastSeekedPosition", window.lastSeekedPosition);
console.log('handlePlay: Using lastSeekedPosition', window.lastSeekedPosition);
video.currentTime = window.lastSeekedPosition;
}
}
@ -683,12 +676,12 @@ const useVideoTrimmer = () => {
.then(() => {
setIsPlaying(true);
// Reset lastSeekedPosition after successful play
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
window.lastSeekedPosition = 0;
}
})
.catch((err) => {
console.error("Error playing video:", err);
console.error('Error playing video:', err);
setIsPlaying(false); // Reset state if play failed
});
}
@ -710,28 +703,28 @@ const useVideoTrimmer = () => {
// Create the JSON data for saving
const saveData = {
type: "save",
type: 'save',
segments: sortedSegments.map((segment) => ({
startTime: formatDetailedTime(segment.startTime),
endTime: formatDetailedTime(segment.endTime)
}))
endTime: formatDetailedTime(segment.endTime),
})),
};
// Display JSON in alert (for demonstration purposes)
if (process.env.NODE_ENV === "development") {
console.debug("Saving data:", saveData);
if (process.env.NODE_ENV === 'development') {
console.debug('Saving data:', saveData);
}
// Mark as saved - no unsaved changes
setHasUnsavedChanges(false);
// Debug message
if (process.env.NODE_ENV === "development") {
console.debug("Changes saved - reset unsaved changes flag");
if (process.env.NODE_ENV === 'development') {
console.debug('Changes saved - reset unsaved changes flag');
}
// Save to history with special "save" action to mark saved state
saveState("save");
saveState('save');
// In a real implementation, this would make a POST request to save the data
// logger.debug("Save data:", saveData);
@ -744,28 +737,28 @@ const useVideoTrimmer = () => {
// Create the JSON data for saving as a copy
const saveData = {
type: "save_as_a_copy",
type: 'save_as_a_copy',
segments: sortedSegments.map((segment) => ({
startTime: formatDetailedTime(segment.startTime),
endTime: formatDetailedTime(segment.endTime)
}))
endTime: formatDetailedTime(segment.endTime),
})),
};
// Display JSON in alert (for demonstration purposes)
if (process.env.NODE_ENV === "development") {
console.debug("Saving data as copy:", saveData);
if (process.env.NODE_ENV === 'development') {
console.debug('Saving data as copy:', saveData);
}
// Mark as saved - no unsaved changes
setHasUnsavedChanges(false);
// Debug message
if (process.env.NODE_ENV === "development") {
console.debug("Changes saved as copy - reset unsaved changes flag");
if (process.env.NODE_ENV === 'development') {
console.debug('Changes saved as copy - reset unsaved changes flag');
}
// Save to history with special "save_copy" action to mark saved state
saveState("save_copy");
saveState('save_copy');
};
// Handle save segments individually action
@ -775,27 +768,27 @@ const useVideoTrimmer = () => {
// Create the JSON data for saving individual segments
const saveData = {
type: "save_segments",
type: 'save_segments',
segments: sortedSegments.map((segment) => ({
name: segment.name,
startTime: formatDetailedTime(segment.startTime),
endTime: formatDetailedTime(segment.endTime)
}))
endTime: formatDetailedTime(segment.endTime),
})),
};
// Display JSON in alert (for demonstration purposes)
if (process.env.NODE_ENV === "development") {
console.debug("Saving data as segments:", saveData);
if (process.env.NODE_ENV === 'development') {
console.debug('Saving data as segments:', saveData);
}
// Mark as saved - no unsaved changes
setHasUnsavedChanges(false);
// Debug message
logger.debug("All segments saved individually - reset unsaved changes flag");
logger.debug('All segments saved individually - reset unsaved changes flag');
// Save to history with special "save_segments" action to mark saved state
saveState("save_segments");
saveState('save_segments');
};
// Handle seeking with mobile check
@ -808,10 +801,8 @@ const useVideoTrimmer = () => {
// Check if device is mobile
const isMobile =
typeof window !== "undefined" &&
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i.test(
navigator.userAgent
);
typeof window !== 'undefined' &&
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i.test(navigator.userAgent);
// Add videoInitialized state
const [videoInitialized, setVideoInitialized] = useState(false);
@ -845,9 +836,9 @@ const useVideoTrimmer = () => {
// If video is somehow paused, ensure it keeps playing
if (video.paused) {
logger.debug("Ensuring playback continues to next segment");
logger.debug('Ensuring playback continues to next segment');
video.play().catch((err) => {
console.error("Error continuing segment playback:", err);
console.error('Error continuing segment playback:', err);
});
}
} else {
@ -855,12 +846,12 @@ const useVideoTrimmer = () => {
video.pause();
setIsPlayingSegments(false);
setCurrentSegmentIndex(0);
video.removeEventListener("timeupdate", handleSegmentsPlayback);
video.removeEventListener('timeupdate', handleSegmentsPlayback);
}
}
};
video.addEventListener("timeupdate", handleSegmentsPlayback);
video.addEventListener('timeupdate', handleSegmentsPlayback);
// Start playing if not already playing
if (video.paused && orderedSegments.length > 0) {
@ -869,7 +860,7 @@ const useVideoTrimmer = () => {
}
return () => {
video.removeEventListener("timeupdate", handleSegmentsPlayback);
video.removeEventListener('timeupdate', handleSegmentsPlayback);
};
}, [isPlayingSegments, currentSegmentIndex, clipSegments]);
@ -878,20 +869,15 @@ const useVideoTrimmer = () => {
const handleSegmentIndexUpdate = (event: CustomEvent) => {
const { segmentIndex } = event.detail;
if (isPlayingSegments && segmentIndex !== currentSegmentIndex) {
logger.debug(
`Updating current segment index from ${currentSegmentIndex} to ${segmentIndex}`
);
logger.debug(`Updating current segment index from ${currentSegmentIndex} to ${segmentIndex}`);
setCurrentSegmentIndex(segmentIndex);
}
};
document.addEventListener("update-segment-index", handleSegmentIndexUpdate as EventListener);
document.addEventListener('update-segment-index', handleSegmentIndexUpdate as EventListener);
return () => {
document.removeEventListener(
"update-segment-index",
handleSegmentIndexUpdate as EventListener
);
document.removeEventListener('update-segment-index', handleSegmentIndexUpdate as EventListener);
};
}, [isPlayingSegments, currentSegmentIndex]);
@ -920,11 +906,11 @@ const useVideoTrimmer = () => {
// Start playback with proper error handling
video.play().catch((err) => {
console.error("Error starting segments playback:", err);
console.error('Error starting segments playback:', err);
setIsPlayingSegments(false);
});
logger.debug("Starting playback of all segments continuously");
logger.debug('Starting playback of all segments continuously');
}
};
@ -960,7 +946,7 @@ const useVideoTrimmer = () => {
handleSaveSegments,
isMobile,
videoInitialized,
setVideoInitialized
setVideoInitialized,
};
};

View File

@ -7,7 +7,7 @@ const logger = {
* Logs debug messages only in development environment
*/
debug: (...args: any[]) => {
if (process.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === 'development') {
console.debug(...args);
}
},
@ -25,7 +25,7 @@ const logger = {
/**
* Always logs info messages
*/
info: (...args: any[]) => console.info(...args)
info: (...args: any[]) => console.info(...args),
};
export default logger;

View File

@ -1,4 +1,4 @@
import { QueryClient, QueryFunction } from "@tanstack/react-query";
import { QueryClient, QueryFunction } from '@tanstack/react-query';
async function throwIfResNotOk(res: Response) {
if (!res.ok) {
@ -7,31 +7,27 @@ async function throwIfResNotOk(res: Response) {
}
}
export async function apiRequest(
method: string,
url: string,
data?: unknown | undefined
): Promise<Response> {
export async function apiRequest(method: string, url: string, data?: unknown | undefined): Promise<Response> {
const res = await fetch(url, {
method,
headers: data ? { "Content-Type": "application/json" } : {},
headers: data ? { 'Content-Type': 'application/json' } : {},
body: data ? JSON.stringify(data) : undefined,
credentials: "include"
credentials: 'include',
});
await throwIfResNotOk(res);
return res;
}
type UnauthorizedBehavior = "returnNull" | "throw";
type UnauthorizedBehavior = 'returnNull' | 'throw';
export const getQueryFn: <T>(options: { on401: UnauthorizedBehavior }) => QueryFunction<T> =
({ on401: unauthorizedBehavior }) =>
async ({ queryKey }) => {
const res = await fetch(queryKey[0] as string, {
credentials: "include"
credentials: 'include',
});
if (unauthorizedBehavior === "returnNull" && res.status === 401) {
if (unauthorizedBehavior === 'returnNull' && res.status === 401) {
return null;
}
@ -42,14 +38,14 @@ export const getQueryFn: <T>(options: { on401: UnauthorizedBehavior }) => QueryF
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: getQueryFn({ on401: "throw" }),
queryFn: getQueryFn({ on401: 'throw' }),
refetchInterval: false,
refetchOnWindowFocus: false,
staleTime: Infinity,
retry: false
retry: false,
},
mutations: {
retry: false
}
}
retry: false,
},
},
});

View File

@ -2,17 +2,17 @@
* Format seconds to HH:MM:SS.mmm format with millisecond precision
*/
export const formatDetailedTime = (seconds: number): string => {
if (isNaN(seconds)) return "00:00:00.000";
if (isNaN(seconds)) return '00:00:00.000';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60);
const milliseconds = Math.round((seconds % 1) * 1000);
const formattedHours = String(hours).padStart(2, "0");
const formattedMinutes = String(minutes).padStart(2, "0");
const formattedSeconds = String(remainingSeconds).padStart(2, "0");
const formattedMilliseconds = String(milliseconds).padStart(3, "0");
const formattedHours = String(hours).padStart(2, '0');
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
const formattedMilliseconds = String(milliseconds).padStart(3, '0');
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}.${formattedMilliseconds}`;
};

View File

@ -1,5 +1,5 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));

View File

@ -20,17 +20,14 @@ export const generateSolidColor = (time: number, duration: number): string => {
* Legacy function kept for compatibility
* Now returns a data URL for a solid color square instead of a video thumbnail
*/
export const generateThumbnail = async (
videoElement: HTMLVideoElement,
time: number
): Promise<string> => {
export const generateThumbnail = async (videoElement: HTMLVideoElement, time: number): Promise<string> => {
return new Promise((resolve) => {
// Create a small canvas for the solid color
const canvas = document.createElement("canvas");
const canvas = document.createElement('canvas');
canvas.width = 10; // Much smaller - we only need a color
canvas.height = 10;
const ctx = canvas.getContext("2d");
const ctx = canvas.getContext('2d');
if (ctx) {
// Get the solid color based on time
const color = generateSolidColor(time, videoElement.duration);
@ -41,7 +38,7 @@ export const generateThumbnail = async (
}
// Convert to data URL (much smaller now)
const dataUrl = canvas.toDataURL("image/png", 0.5);
const dataUrl = canvas.toDataURL('image/png', 0.5);
resolve(dataUrl);
});
};

View File

@ -22,16 +22,13 @@ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// For now, we'll use a mock API that returns a promise
// This can be replaced with actual API calls later
export const trimVideo = async (
mediaId: string,
data: TrimVideoRequest
): Promise<TrimVideoResponse> => {
export const trimVideo = async (mediaId: string, data: TrimVideoRequest): Promise<TrimVideoResponse> => {
try {
// Attempt the real API call
const response = await fetch(`/api/v1/media/${mediaId}/trim_video`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
@ -43,17 +40,17 @@ export const trimVideo = async (
const errorData = await response.json();
return {
status: 400,
error: errorData.error || "An error occurred during processing",
msg: "Video Processing Error",
url_redirect: ""
error: errorData.error || 'An error occurred during processing',
msg: 'Video Processing Error',
url_redirect: '',
};
} catch (parseError) {
// If can't parse response JSON, return generic error
return {
status: 400,
error: "An error occurred during video processing",
msg: "Video Processing Error",
url_redirect: ""
error: 'An error occurred during video processing',
msg: 'Video Processing Error',
url_redirect: '',
};
}
} else if (response.status !== 404) {
@ -63,17 +60,17 @@ export const trimVideo = async (
const errorData = await response.json();
return {
status: response.status,
error: errorData.error || "An error occurred during processing",
msg: "Video Processing Error",
url_redirect: ""
error: errorData.error || 'An error occurred during processing',
msg: 'Video Processing Error',
url_redirect: '',
};
} catch (parseError) {
// If can't parse response JSON, return generic error
return {
status: response.status,
error: "An error occurred during video processing",
msg: "Video Processing Error",
url_redirect: ""
error: 'An error occurred during video processing',
msg: 'Video Processing Error',
url_redirect: '',
};
}
} else {
@ -81,8 +78,8 @@ export const trimVideo = async (
await delay(1500); // Simulate 1.5 second server delay
return {
status: 200, // Mock success status
msg: "Video Processed Successfully", // Updated per requirements
url_redirect: `./view?m=${mediaId}`
msg: 'Video Processed Successfully', // Updated per requirements
url_redirect: `./view?m=${mediaId}`,
};
}
}
@ -91,17 +88,17 @@ export const trimVideo = async (
const jsonResponse = await response.json();
return {
status: 200,
msg: "Video Processed Successfully", // Ensure the success message is correct
msg: 'Video Processed Successfully', // Ensure the success message is correct
url_redirect: jsonResponse.url_redirect || `./view?m=${mediaId}`,
...jsonResponse
...jsonResponse,
};
} catch (error) {
// For any fetch errors, return mock success response with delay
await delay(1500); // Simulate 1.5 second server delay
return {
status: 200, // Mock success status
msg: "Video Processed Successfully", // Consistent with requirements
url_redirect: `./view?m=${mediaId}`
msg: 'Video Processed Successfully', // Consistent with requirements
url_redirect: `./view?m=${mediaId}`,
};
}

View File

@ -3788,9 +3788,9 @@
}
},
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
"integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
"license": "ISC",
"optional": true
},
@ -12590,9 +12590,9 @@
}
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
"devOptional": true,
"license": "MIT"
},

File diff suppressed because it is too large Load Diff