mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-06 07:28:53 -05:00
fix: Disable Segment Tools and Reset Preview State During Playback
This commit is contained in:
parent
a5acce4ab1
commit
1505a155e6
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ yt.readme.md
|
|||||||
frontend-tools/.DS_Store
|
frontend-tools/.DS_Store
|
||||||
static/video_editor/videos/sample-video-30s.mp4
|
static/video_editor/videos/sample-video-30s.mp4
|
||||||
static/video_editor/videos/sample-video-37s.mp4
|
static/video_editor/videos/sample-video-37s.mp4
|
||||||
|
/frontend-tools/video-editor-v2
|
||||||
|
.DS_Store
|
||||||
|
|||||||
3
frontend-tools/video-editor/.gitignore
vendored
3
frontend-tools/video-editor/.gitignore
vendored
@ -10,3 +10,6 @@ client/public/videos/sample-video-30s.mp4
|
|||||||
client/public/videos/sample-video-37s.mp4
|
client/public/videos/sample-video-37s.mp4
|
||||||
videos/sample-video-37s.mp4
|
videos/sample-video-37s.mp4
|
||||||
client/public/videos/sample-video-30s.mp4
|
client/public/videos/sample-video-30s.mp4
|
||||||
|
client/public/videos/sample-video-1.mp4
|
||||||
|
client/public/videos/sample-video-10m.mp4
|
||||||
|
client/public/videos/sample-video-10s.mp4
|
||||||
|
|||||||
@ -16,7 +16,6 @@ const App = () => {
|
|||||||
isPlaying,
|
isPlaying,
|
||||||
setIsPlaying,
|
setIsPlaying,
|
||||||
isMuted,
|
isMuted,
|
||||||
isPreviewMode,
|
|
||||||
thumbnails,
|
thumbnails,
|
||||||
trimStart,
|
trimStart,
|
||||||
trimEnd,
|
trimEnd,
|
||||||
@ -34,7 +33,6 @@ const App = () => {
|
|||||||
handleReset,
|
handleReset,
|
||||||
handleUndo,
|
handleUndo,
|
||||||
handleRedo,
|
handleRedo,
|
||||||
handlePreview,
|
|
||||||
toggleMute,
|
toggleMute,
|
||||||
handleSave,
|
handleSave,
|
||||||
handleSaveACopy,
|
handleSaveACopy,
|
||||||
@ -251,10 +249,8 @@ const App = () => {
|
|||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
onUndo={handleUndo}
|
onUndo={handleUndo}
|
||||||
onRedo={handleRedo}
|
onRedo={handleRedo}
|
||||||
onPreview={handlePreview}
|
|
||||||
onPlaySegments={handlePlaySegments}
|
onPlaySegments={handlePlaySegments}
|
||||||
onPlay={handlePlay}
|
onPlay={handlePlay}
|
||||||
isPreviewMode={isPreviewMode}
|
|
||||||
isPlaying={isPlaying}
|
isPlaying={isPlaying}
|
||||||
isPlayingSegments={isPlayingSegments}
|
isPlayingSegments={isPlayingSegments}
|
||||||
canUndo={historyPosition > 0}
|
canUndo={historyPosition > 0}
|
||||||
@ -279,7 +275,6 @@ const App = () => {
|
|||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onSaveACopy={handleSaveACopy}
|
onSaveACopy={handleSaveACopy}
|
||||||
onSaveSegments={handleSaveSegments}
|
onSaveSegments={handleSaveSegments}
|
||||||
isPreviewMode={isPreviewMode}
|
|
||||||
hasUnsavedChanges={hasUnsavedChanges}
|
hasUnsavedChanges={hasUnsavedChanges}
|
||||||
isIOSUninitialized={isMobile && !videoInitialized}
|
isIOSUninitialized={isMobile && !videoInitialized}
|
||||||
isPlaying={isPlaying}
|
isPlaying={isPlaying}
|
||||||
|
|||||||
@ -6,12 +6,10 @@ interface EditingToolsProps {
|
|||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
onUndo: () => void;
|
onUndo: () => void;
|
||||||
onRedo: () => void;
|
onRedo: () => void;
|
||||||
onPreview: () => void;
|
|
||||||
onPlaySegments: () => void;
|
onPlaySegments: () => void;
|
||||||
onPlay: () => void;
|
onPlay: () => void;
|
||||||
canUndo: boolean;
|
canUndo: boolean;
|
||||||
canRedo: boolean;
|
canRedo: boolean;
|
||||||
isPreviewMode?: boolean;
|
|
||||||
isPlaying?: boolean;
|
isPlaying?: boolean;
|
||||||
isPlayingSegments?: boolean;
|
isPlayingSegments?: boolean;
|
||||||
}
|
}
|
||||||
@ -21,12 +19,10 @@ const EditingTools = ({
|
|||||||
onReset,
|
onReset,
|
||||||
onUndo,
|
onUndo,
|
||||||
onRedo,
|
onRedo,
|
||||||
onPreview,
|
|
||||||
onPlaySegments,
|
onPlaySegments,
|
||||||
onPlay,
|
onPlay,
|
||||||
canUndo,
|
canUndo,
|
||||||
canRedo,
|
canRedo,
|
||||||
isPreviewMode = false,
|
|
||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
isPlayingSegments = false,
|
isPlayingSegments = false,
|
||||||
}: EditingToolsProps) => {
|
}: EditingToolsProps) => {
|
||||||
@ -116,8 +112,8 @@ const EditingTools = ({
|
|||||||
)}
|
)}
|
||||||
</button> */}
|
</button> */}
|
||||||
|
|
||||||
{/* Standard Play button (only shown when not in preview mode or segments playback) */}
|
{/* Standard Play button (only shown when not in segments playback on small screens) */}
|
||||||
{!isPreviewMode && (!isPlayingSegments || !isSmallScreen) && (
|
{(!isPlayingSegments || !isSmallScreen) && (
|
||||||
<button
|
<button
|
||||||
className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
|
className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
|
||||||
onClick={handlePlay}
|
onClick={handlePlay}
|
||||||
@ -125,7 +121,7 @@ const EditingTools = ({
|
|||||||
style={{ fontSize: '0.875rem' }}
|
style={{ fontSize: '0.875rem' }}
|
||||||
disabled={isPlayingSegments}
|
disabled={isPlayingSegments}
|
||||||
>
|
>
|
||||||
{isPlaying ? (
|
{isPlaying && !isPlayingSegments ? (
|
||||||
<>
|
<>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const IOSVideoPlayer = ({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to sample video if needed
|
// Fallback to sample video if needed
|
||||||
setVideoUrl("/videos/sample-video-37s.mp4");
|
setVideoUrl("/videos/sample-video-10m.mp4");
|
||||||
}
|
}
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,6 @@ interface TimelineControlsProps {
|
|||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
onSaveACopy?: () => void;
|
onSaveACopy?: () => void;
|
||||||
onSaveSegments?: () => void;
|
onSaveSegments?: () => void;
|
||||||
isPreviewMode?: boolean;
|
|
||||||
hasUnsavedChanges?: boolean;
|
hasUnsavedChanges?: boolean;
|
||||||
isIOSUninitialized?: boolean;
|
isIOSUninitialized?: boolean;
|
||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
@ -116,7 +115,6 @@ const TimelineControls = ({
|
|||||||
onSave,
|
onSave,
|
||||||
onSaveACopy,
|
onSaveACopy,
|
||||||
onSaveSegments,
|
onSaveSegments,
|
||||||
isPreviewMode,
|
|
||||||
hasUnsavedChanges = false,
|
hasUnsavedChanges = false,
|
||||||
isIOSUninitialized = false,
|
isIOSUninitialized = false,
|
||||||
isPlaying,
|
isPlaying,
|
||||||
@ -572,12 +570,11 @@ const TimelineControls = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip if no video or no active segment
|
// Skip if no video or no active segment
|
||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
if (!video || !activeSegment || !isPlayingSegment || isPreviewMode) {
|
if (!video || !activeSegment || !isPlayingSegment) {
|
||||||
// Log why we're skipping
|
// Log why we're skipping
|
||||||
if (!video) logger.debug("Skipping segment boundary check - no video element");
|
if (!video) logger.debug("Skipping segment boundary check - no video element");
|
||||||
else if (!activeSegment) logger.debug("Skipping segment boundary check - no active segment");
|
else if (!activeSegment) logger.debug("Skipping segment boundary check - no active segment");
|
||||||
else if (!isPlayingSegment) logger.debug("Skipping segment boundary check - not in segment playback mode");
|
else if (!isPlayingSegment) logger.debug("Skipping segment boundary check - not in segment playback mode");
|
||||||
else if (isPreviewMode) logger.debug("Skipping segment boundary check in preview mode");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +661,7 @@ const TimelineControls = ({
|
|||||||
video.removeEventListener('timeupdate', handleTimeUpdate);
|
video.removeEventListener('timeupdate', handleTimeUpdate);
|
||||||
logger.debug("Segment boundary check DEACTIVATED");
|
logger.debug("Segment boundary check DEACTIVATED");
|
||||||
};
|
};
|
||||||
}, [activeSegment, isPlayingSegment, isPreviewMode, continuePastBoundary, clipSegments]);
|
}, [activeSegment, isPlayingSegment, continuePastBoundary, clipSegments]);
|
||||||
|
|
||||||
// Update display time and check for transitions between segments and empty spaces
|
// Update display time and check for transitions between segments and empty spaces
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -1231,10 +1228,8 @@ const TimelineControls = ({
|
|||||||
setIsPlayingSegment(false);
|
setIsPlayingSegment(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Resume playback in two cases (but not during segments playback):
|
// Resume playback if it was playing before (but not during segments playback)
|
||||||
// 1. If it was playing before (regular playback)
|
else if (wasPlaying && !isPlayingSegments) {
|
||||||
// 2. If we're in preview mode (regardless of previous playing state)
|
|
||||||
else if ((wasPlaying || isPreviewMode) && !isPlayingSegments) {
|
|
||||||
logger.debug("Resuming playback after timeline click");
|
logger.debug("Resuming playback after timeline click");
|
||||||
videoRef.current.play()
|
videoRef.current.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -1581,17 +1576,7 @@ const TimelineControls = ({
|
|||||||
else if (!wasInsideSegmentBefore && isInsideSegment) {
|
else if (!wasInsideSegmentBefore && isInsideSegment) {
|
||||||
logger.debug("Playhead moved INTO segment during drag - can start playback");
|
logger.debug("Playhead moved INTO segment during drag - can start playback");
|
||||||
setActiveSegment(segment);
|
setActiveSegment(segment);
|
||||||
// In preview mode, we automatically start playing when playhead enters segment
|
// Removed automatic playback - user needs to manually start playback
|
||||||
if (isPreviewMode) {
|
|
||||||
videoRef.current.play()
|
|
||||||
.then(() => {
|
|
||||||
setIsPlayingSegment(true);
|
|
||||||
logger.debug("Started playback after dragging segment to include playhead");
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("Error starting playback:", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Another special case: playhead was inside segment before, but now is also inside but at a different position
|
// Another special case: playhead was inside segment before, but now is also inside but at a different position
|
||||||
else if (wasInsideSegmentBefore && isInsideSegment &&
|
else if (wasInsideSegmentBefore && isInsideSegment &&
|
||||||
@ -1738,8 +1723,8 @@ const TimelineControls = ({
|
|||||||
console.error("Error continuing segments playback after segment click:", err);
|
console.error("Error continuing segments playback after segment click:", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// If video was playing before OR we're in preview mode, ensure it continues playing (but not in segments mode)
|
// If video was playing before, ensure it continues playing (but not in segments mode)
|
||||||
else if ((wasPlaying || isPreviewMode) && !isPlayingSegments) {
|
else if (wasPlaying && !isPlayingSegments) {
|
||||||
// Set current segment as active segment for boundary checking
|
// Set current segment as active segment for boundary checking
|
||||||
setActiveSegment(segment);
|
setActiveSegment(segment);
|
||||||
// Reset the continuePastBoundary flag when clicking on a segment to ensure boundaries work
|
// Reset the continuePastBoundary flag when clicking on a segment to ensure boundaries work
|
||||||
@ -1754,18 +1739,7 @@ const TimelineControls = ({
|
|||||||
console.error("Error resuming playback after segment click:", err);
|
console.error("Error resuming playback after segment click:", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Always continue playback in preview mode, even if video was paused when clicking (but not in segments mode)
|
// Removed preview mode playback - preview functionality replaced by segments playback
|
||||||
else if (isPreviewMode && !isPlayingSegments) {
|
|
||||||
setActiveSegment(segment);
|
|
||||||
videoRef.current.play()
|
|
||||||
.then(() => {
|
|
||||||
setIsPlayingSegment(true);
|
|
||||||
logger.debug("Continued preview playback after segment click");
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("Error continuing preview playback:", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate tooltip position directly above click point
|
// Calculate tooltip position directly above click point
|
||||||
@ -2448,15 +2422,16 @@ const TimelineControls = ({
|
|||||||
{/* First row with time adjustment buttons */}
|
{/* First row with time adjustment buttons */}
|
||||||
<div className="tooltip-row">
|
<div className="tooltip-row">
|
||||||
<button
|
<button
|
||||||
className="tooltip-time-btn"
|
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Seek -50ms (click or hold)"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek -50ms (click or hold)"}
|
||||||
{...handleContinuousTimeAdjustment(-0.05)}
|
disabled={isPlayingSegments}
|
||||||
|
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(-0.05) : {})}
|
||||||
style={{
|
style={{
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
WebkitUserSelect: 'none',
|
WebkitUserSelect: 'none',
|
||||||
WebkitTouchCallout: 'none',
|
WebkitTouchCallout: 'none',
|
||||||
touchAction: 'manipulation',
|
touchAction: 'manipulation',
|
||||||
cursor: 'pointer',
|
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||||
WebkitTapHighlightColor: 'transparent'
|
WebkitTapHighlightColor: 'transparent'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -2464,15 +2439,16 @@ const TimelineControls = ({
|
|||||||
</button>
|
</button>
|
||||||
<div className="tooltip-time-display">{formatDetailedTime(displayTime)}</div>
|
<div className="tooltip-time-display">{formatDetailedTime(displayTime)}</div>
|
||||||
<button
|
<button
|
||||||
className="tooltip-time-btn"
|
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Seek +50ms (click or hold)"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek +50ms (click or hold)"}
|
||||||
{...handleContinuousTimeAdjustment(0.05)}
|
disabled={isPlayingSegments}
|
||||||
|
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(0.05) : {})}
|
||||||
style={{
|
style={{
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
WebkitUserSelect: 'none',
|
WebkitUserSelect: 'none',
|
||||||
WebkitTouchCallout: 'none',
|
WebkitTouchCallout: 'none',
|
||||||
touchAction: 'manipulation',
|
touchAction: 'manipulation',
|
||||||
cursor: 'pointer',
|
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||||
WebkitTapHighlightColor: 'transparent'
|
WebkitTapHighlightColor: 'transparent'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -2483,8 +2459,9 @@ const TimelineControls = ({
|
|||||||
{/* Second row with action buttons */}
|
{/* Second row with action buttons */}
|
||||||
<div className="tooltip-row tooltip-actions">
|
<div className="tooltip-row tooltip-actions">
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn delete"
|
className={`tooltip-action-btn delete ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Delete segment"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Delete segment"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// Call the delete segment function with the current segment ID
|
// Call the delete segment function with the current segment ID
|
||||||
@ -2505,8 +2482,9 @@ const TimelineControls = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn scissors"
|
className={`tooltip-action-btn scissors ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Split segment at current position"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Split segment at current position"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// Call the split segment function with the current segment ID and time
|
// Call the split segment function with the current segment ID and time
|
||||||
@ -2530,8 +2508,9 @@ const TimelineControls = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn play-from-start"
|
className={`tooltip-action-btn play-from-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Play segment from beginning"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Play segment from beginning"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -2626,8 +2605,9 @@ const TimelineControls = ({
|
|||||||
|
|
||||||
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
||||||
<button
|
<button
|
||||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'}`}
|
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'} ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip={isPlaying ? "Pause playback" : "Play from current position"}
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : (isPlaying ? "Pause playback" : "Play from current position")}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -2654,8 +2634,9 @@ const TimelineControls = ({
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn set-in"
|
className={`tooltip-action-btn set-in ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Set start point at current position"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Set start point at current position"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -2692,8 +2673,9 @@ const TimelineControls = ({
|
|||||||
<img src={segmentStartIcon} alt="Set start point" style={{width: '24px', height: '24px'}} />
|
<img src={segmentStartIcon} alt="Set start point" style={{width: '24px', height: '24px'}} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn set-out"
|
className={`tooltip-action-btn set-out ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Set end point at current position"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Set end point at current position"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -2745,17 +2727,35 @@ const TimelineControls = ({
|
|||||||
{/* First row with time adjustment buttons - same as segment tooltip */}
|
{/* First row with time adjustment buttons - same as segment tooltip */}
|
||||||
<div className="tooltip-row">
|
<div className="tooltip-row">
|
||||||
<button
|
<button
|
||||||
className="tooltip-time-btn"
|
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Seek -50ms (click or hold)"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek -50ms (click or hold)"}
|
||||||
{...handleContinuousTimeAdjustment(-0.05)}
|
disabled={isPlayingSegments}
|
||||||
|
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(-0.05) : {})}
|
||||||
|
style={{
|
||||||
|
userSelect: 'none',
|
||||||
|
WebkitUserSelect: 'none',
|
||||||
|
WebkitTouchCallout: 'none',
|
||||||
|
touchAction: 'manipulation',
|
||||||
|
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||||
|
WebkitTapHighlightColor: 'transparent'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
-50ms
|
-50ms
|
||||||
</button>
|
</button>
|
||||||
<div className="tooltip-time-display">{formatDetailedTime(clickedTime)}</div>
|
<div className="tooltip-time-display">{formatDetailedTime(clickedTime)}</div>
|
||||||
<button
|
<button
|
||||||
className="tooltip-time-btn"
|
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Seek +50ms (click or hold)"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek +50ms (click or hold)"}
|
||||||
{...handleContinuousTimeAdjustment(0.05)}
|
disabled={isPlayingSegments}
|
||||||
|
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(0.05) : {})}
|
||||||
|
style={{
|
||||||
|
userSelect: 'none',
|
||||||
|
WebkitUserSelect: 'none',
|
||||||
|
WebkitTouchCallout: 'none',
|
||||||
|
touchAction: 'manipulation',
|
||||||
|
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||||
|
WebkitTapHighlightColor: 'transparent'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
+50ms
|
+50ms
|
||||||
</button>
|
</button>
|
||||||
@ -2765,9 +2765,9 @@ const TimelineControls = ({
|
|||||||
<div className="tooltip-row tooltip-actions">
|
<div className="tooltip-row tooltip-actions">
|
||||||
{/* New segment button - Moved to first position */}
|
{/* New segment button - Moved to first position */}
|
||||||
<button
|
<button
|
||||||
className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 ? 'disabled' : ''}`}
|
className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 || isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip={availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment'}
|
data-tooltip={isPlayingSegments ? 'Disabled during preview' : (availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment')}
|
||||||
disabled={availableSegmentDuration < 0.5}
|
disabled={availableSegmentDuration < 0.5 || isPlayingSegments}
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -2831,8 +2831,9 @@ const TimelineControls = ({
|
|||||||
|
|
||||||
{/* Go to start button - play from beginning of cutaway (until next segment) */}
|
{/* Go to start button - play from beginning of cutaway (until next segment) */}
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn play-from-start"
|
className={`tooltip-action-btn play-from-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Play from beginning of cutaway"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Play from beginning of cutaway"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -3283,8 +3284,9 @@ const TimelineControls = ({
|
|||||||
|
|
||||||
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
||||||
<button
|
<button
|
||||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'}`}
|
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'} ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip={isPlaying ? "Pause playback" : "Play from here until next segment"}
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : (isPlaying ? "Pause playback" : "Play from here until next segment")}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -3312,8 +3314,9 @@ const TimelineControls = ({
|
|||||||
|
|
||||||
{/* Segment end adjustment button (always shown) */}
|
{/* Segment end adjustment button (always shown) */}
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn segment-end"
|
className={`tooltip-action-btn segment-end ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Adjust end of previous segment"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Adjust end of previous segment"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@ -3489,8 +3492,9 @@ const TimelineControls = ({
|
|||||||
|
|
||||||
{/* Segment start adjustment button (always shown) */}
|
{/* Segment start adjustment button (always shown) */}
|
||||||
<button
|
<button
|
||||||
className="tooltip-action-btn segment-start"
|
className={`tooltip-action-btn segment-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||||
data-tooltip="Adjust start of next segment"
|
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Adjust start of next segment"}
|
||||||
|
disabled={isPlayingSegments}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
|
|
||||||
const sampleVideoUrl = typeof window !== 'undefined' &&
|
const sampleVideoUrl = typeof window !== 'undefined' &&
|
||||||
(window as any).MEDIA_DATA?.videoUrl ||
|
(window as any).MEDIA_DATA?.videoUrl ||
|
||||||
"/videos/sample-video-37s.mp4";
|
"/videos/sample-video-10m.mp4";
|
||||||
|
|
||||||
// Detect iOS device
|
// Detect iOS device
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -21,9 +21,7 @@ const useVideoTrimmer = () => {
|
|||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [isMuted, setIsMuted] = useState(false);
|
const [isMuted, setIsMuted] = useState(false);
|
||||||
|
|
||||||
// Preview mode state for playing only segments
|
// Removed preview mode - replaced by isPlayingSegments
|
||||||
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
|
||||||
const [previewSegmentIndex, setPreviewSegmentIndex] = useState(0);
|
|
||||||
|
|
||||||
// Timeline state
|
// Timeline state
|
||||||
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
||||||
@ -146,18 +144,12 @@ const useVideoTrimmer = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePlay = () => {
|
const handlePlay = () => {
|
||||||
// Only update isPlaying if we're not in preview mode
|
setIsPlaying(true);
|
||||||
if (!isPreviewMode) {
|
setVideoInitialized(true);
|
||||||
setIsPlaying(true);
|
|
||||||
setVideoInitialized(true);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePause = () => {
|
const handlePause = () => {
|
||||||
// Only update isPlaying if we're not in preview mode
|
setIsPlaying(false);
|
||||||
if (!isPreviewMode) {
|
|
||||||
setIsPlaying(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEnded = () => {
|
const handleEnded = () => {
|
||||||
@ -180,7 +172,7 @@ const useVideoTrimmer = () => {
|
|||||||
video.removeEventListener('pause', handlePause);
|
video.removeEventListener('pause', handlePause);
|
||||||
video.removeEventListener('ended', handleEnded);
|
video.removeEventListener('ended', handleEnded);
|
||||||
};
|
};
|
||||||
}, [isPreviewMode]);
|
}, []);
|
||||||
|
|
||||||
// Play/pause video
|
// Play/pause video
|
||||||
const playPauseVideo = () => {
|
const playPauseVideo = () => {
|
||||||
@ -226,9 +218,6 @@ const useVideoTrimmer = () => {
|
|||||||
// Track if the video was playing before seeking
|
// Track if the video was playing before seeking
|
||||||
const wasPlaying = !video.paused;
|
const wasPlaying = !video.paused;
|
||||||
|
|
||||||
// Store current preview mode state to preserve it
|
|
||||||
const wasInPreviewMode = isPreviewMode;
|
|
||||||
|
|
||||||
// Update the video position
|
// Update the video position
|
||||||
video.currentTime = time;
|
video.currentTime = time;
|
||||||
setCurrentTime(time);
|
setCurrentTime(time);
|
||||||
@ -239,36 +228,12 @@ const useVideoTrimmer = () => {
|
|||||||
window.lastSeekedPosition = time;
|
window.lastSeekedPosition = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find segment at this position for preview mode playback
|
// Resume playback if it was playing before
|
||||||
if (wasInPreviewMode) {
|
if (wasPlaying) {
|
||||||
const segmentAtPosition = clipSegments.find(
|
|
||||||
seg => time >= seg.startTime && time <= seg.endTime
|
|
||||||
);
|
|
||||||
|
|
||||||
if (segmentAtPosition) {
|
|
||||||
// Update the active segment index in preview mode
|
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
|
||||||
const newSegmentIndex = orderedSegments.findIndex(seg => seg.id === segmentAtPosition.id);
|
|
||||||
if (newSegmentIndex !== -1) {
|
|
||||||
setPreviewSegmentIndex(newSegmentIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume playback in two scenarios:
|
|
||||||
// 1. If it was playing before (regular mode)
|
|
||||||
// 2. If we're in preview mode (regardless of previous state)
|
|
||||||
if (wasPlaying || wasInPreviewMode) {
|
|
||||||
// Ensure preview mode stays on if it was on before
|
|
||||||
if (wasInPreviewMode) {
|
|
||||||
setIsPreviewMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Play immediately without delay
|
// Play immediately without delay
|
||||||
video.play()
|
video.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsPlaying(true); // Update state to reflect we're playing
|
setIsPlaying(true); // Update state to reflect we're playing
|
||||||
// "Resumed playback after seeking in " + (wasInPreviewMode ? "preview" : "regular") + " mode"
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error("Error resuming playback:", err);
|
console.error("Error resuming playback:", err);
|
||||||
@ -550,152 +515,9 @@ const useVideoTrimmer = () => {
|
|||||||
};
|
};
|
||||||
}, [clipSegments, duration]);
|
}, [clipSegments, duration]);
|
||||||
|
|
||||||
// Preview mode effect to handle playing only segments
|
// Removed preview mode effect - functionality replaced by isPlayingSegments
|
||||||
useEffect(() => {
|
|
||||||
if (!isPreviewMode || !videoRef.current) return;
|
|
||||||
|
|
||||||
// Sort segments by start time
|
// Removed preview mode functions - replaced by handlePlaySegments
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
|
||||||
if (orderedSegments.length === 0) return;
|
|
||||||
|
|
||||||
const video = videoRef.current;
|
|
||||||
|
|
||||||
// Function to handle segment playback
|
|
||||||
const handleSegmentPlayback = () => {
|
|
||||||
if (!isPreviewMode || !video) return;
|
|
||||||
|
|
||||||
const currentSegment = orderedSegments[previewSegmentIndex];
|
|
||||||
if (!currentSegment) return;
|
|
||||||
|
|
||||||
const currentTime = video.currentTime;
|
|
||||||
|
|
||||||
// If we're before the current segment's start, jump to it
|
|
||||||
if (currentTime < currentSegment.startTime) {
|
|
||||||
video.currentTime = currentSegment.startTime;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've reached the end of the current segment
|
|
||||||
if (currentTime >= currentSegment.endTime - 0.01) { // Small threshold to ensure smooth transition
|
|
||||||
// Move to the next segment if available
|
|
||||||
if (previewSegmentIndex < orderedSegments.length - 1) {
|
|
||||||
// Play next segment
|
|
||||||
const nextSegment = orderedSegments[previewSegmentIndex + 1];
|
|
||||||
video.currentTime = nextSegment.startTime;
|
|
||||||
setPreviewSegmentIndex(previewSegmentIndex + 1);
|
|
||||||
|
|
||||||
logger.debug("Preview: Moving to next segment", {
|
|
||||||
from: formatDetailedTime(currentSegment.endTime),
|
|
||||||
to: formatDetailedTime(nextSegment.startTime),
|
|
||||||
segmentIndex: previewSegmentIndex + 1
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Loop back to first segment
|
|
||||||
logger.debug("Preview: Looping back to first segment");
|
|
||||||
video.currentTime = orderedSegments[0].startTime;
|
|
||||||
setPreviewSegmentIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure playback continues
|
|
||||||
video.play().catch(err => {
|
|
||||||
console.error("Error continuing preview playback:", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add event listener for timeupdate to check segment boundaries
|
|
||||||
video.addEventListener('timeupdate', handleSegmentPlayback);
|
|
||||||
|
|
||||||
// Start playing if not already playing
|
|
||||||
if (video.paused) {
|
|
||||||
video.currentTime = orderedSegments[previewSegmentIndex].startTime;
|
|
||||||
video.play().catch(err => {
|
|
||||||
console.error("Error starting preview playback:", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (video) {
|
|
||||||
video.removeEventListener('timeupdate', handleSegmentPlayback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [isPreviewMode, previewSegmentIndex, clipSegments]);
|
|
||||||
|
|
||||||
// Handle starting preview mode
|
|
||||||
const handleStartPreview = () => {
|
|
||||||
const video = videoRef.current;
|
|
||||||
if (!video || clipSegments.length === 0) return;
|
|
||||||
|
|
||||||
// If preview is already active, do nothing
|
|
||||||
if (isPreviewMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If normal playback is happening, pause it
|
|
||||||
if (isPlaying) {
|
|
||||||
video.pause();
|
|
||||||
setIsPlaying(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort segments by start time
|
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
|
||||||
if (orderedSegments.length === 0) return;
|
|
||||||
|
|
||||||
// Set the preview mode flag
|
|
||||||
setIsPreviewMode(true);
|
|
||||||
logger.debug("Entering preview mode");
|
|
||||||
|
|
||||||
// Set the first segment as the current one in the preview sequence
|
|
||||||
setPreviewSegmentIndex(0);
|
|
||||||
|
|
||||||
// Move to the start of the first segment
|
|
||||||
video.currentTime = orderedSegments[0].startTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle playing/stopping preview mode
|
|
||||||
const handlePreview = () => {
|
|
||||||
const video = videoRef.current;
|
|
||||||
if (!video || clipSegments.length === 0) return;
|
|
||||||
|
|
||||||
// If preview is already active, turn it off
|
|
||||||
if (isPreviewMode) {
|
|
||||||
setIsPreviewMode(false);
|
|
||||||
|
|
||||||
// Always pause the video when exiting preview mode
|
|
||||||
video.pause();
|
|
||||||
setIsPlaying(false);
|
|
||||||
|
|
||||||
logger.debug("Exiting preview mode - video paused");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort segments by start time
|
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
|
||||||
if (orderedSegments.length === 0) return;
|
|
||||||
|
|
||||||
// Set the preview mode flag
|
|
||||||
setIsPreviewMode(true);
|
|
||||||
logger.debug("Entering preview mode");
|
|
||||||
|
|
||||||
// Set the first segment as the current one in the preview sequence
|
|
||||||
setPreviewSegmentIndex(0);
|
|
||||||
|
|
||||||
// Start preview mode by playing the first segment
|
|
||||||
video.currentTime = orderedSegments[0].startTime;
|
|
||||||
|
|
||||||
// Start playback
|
|
||||||
video.play()
|
|
||||||
.then(() => {
|
|
||||||
setIsPlaying(true);
|
|
||||||
logger.debug("Preview started successfully");
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("Error starting preview:", err);
|
|
||||||
setIsPreviewMode(false);
|
|
||||||
setIsPlaying(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle trim start change
|
// Handle trim start change
|
||||||
const handleTrimStartChange = (time: number) => {
|
const handleTrimStartChange = (time: number) => {
|
||||||
@ -820,16 +642,6 @@ const useVideoTrimmer = () => {
|
|||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
if (!video) return;
|
if (!video) return;
|
||||||
|
|
||||||
// If in preview mode, exit it before toggling normal play
|
|
||||||
if (isPreviewMode) {
|
|
||||||
setIsPreviewMode(false);
|
|
||||||
// Don't immediately start playing when exiting preview mode
|
|
||||||
// Just update the state and return
|
|
||||||
setIsPlaying(false);
|
|
||||||
video.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
// Pause the video
|
// Pause the video
|
||||||
video.pause();
|
video.pause();
|
||||||
@ -1067,10 +879,7 @@ const useVideoTrimmer = () => {
|
|||||||
setIsPlayingSegments(true);
|
setIsPlayingSegments(true);
|
||||||
setCurrentSegmentIndex(0);
|
setCurrentSegmentIndex(0);
|
||||||
|
|
||||||
// Exit preview mode if active
|
// Start segments playback
|
||||||
if (isPreviewMode) {
|
|
||||||
setIsPreviewMode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort segments by start time
|
// Sort segments by start time
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||||
@ -1095,7 +904,6 @@ const useVideoTrimmer = () => {
|
|||||||
isPlaying,
|
isPlaying,
|
||||||
setIsPlaying,
|
setIsPlaying,
|
||||||
isMuted,
|
isMuted,
|
||||||
isPreviewMode,
|
|
||||||
isPlayingSegments,
|
isPlayingSegments,
|
||||||
thumbnails,
|
thumbnails,
|
||||||
trimStart,
|
trimStart,
|
||||||
@ -1114,7 +922,6 @@ const useVideoTrimmer = () => {
|
|||||||
handleReset,
|
handleReset,
|
||||||
handleUndo,
|
handleUndo,
|
||||||
handleRedo,
|
handleRedo,
|
||||||
handlePreview,
|
|
||||||
handlePlaySegments,
|
handlePlaySegments,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
handleSave,
|
handleSave,
|
||||||
|
|||||||
@ -201,18 +201,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style for the preview mode message that replaces the play button */
|
/* Preview mode styles removed - replaced by segments playback mode */
|
||||||
.preview-mode-message {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: rgba(59, 130, 246, 0.1);
|
|
||||||
color: #3b82f6;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
animation: pulse 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
@ -226,13 +215,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-mode-message svg {
|
|
||||||
height: 1.25rem;
|
|
||||||
width: 1.25rem;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
color: #3b82f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add responsive button text class */
|
/* Add responsive button text class */
|
||||||
.button-text {
|
.button-text {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
@ -340,11 +322,7 @@
|
|||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smaller preview mode message */
|
/* Removed preview mode message styles */
|
||||||
.preview-mode-message {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
padding: 4px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
margin: 0 0.25rem;
|
margin: 0 0.25rem;
|
||||||
|
|||||||
@ -814,43 +814,7 @@
|
|||||||
100% { opacity: 0.7; transform: scale(1); }
|
100% { opacity: 0.7; transform: scale(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Preview mode styles */
|
/* Preview mode styles removed - replaced by segments playback mode */
|
||||||
.preview-mode .tooltip-action-btn {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-mode .tooltip-time-btn {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Timeline preview mode styles */
|
|
||||||
.timeline-container-card.preview-mode {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-container-card.preview-mode .timeline-marker-head,
|
|
||||||
.timeline-container-card.preview-mode .timeline-marker-drag,
|
|
||||||
.timeline-container-card.preview-mode .clip-segment,
|
|
||||||
.timeline-container-card.preview-mode .clip-segment-handle,
|
|
||||||
.timeline-container-card.preview-mode .time-button,
|
|
||||||
.timeline-container-card.preview-mode .zoom-button,
|
|
||||||
.timeline-container-card.preview-mode .save-button,
|
|
||||||
.timeline-container-card.preview-mode .save-copy-button,
|
|
||||||
.timeline-container-card.preview-mode .save-segments-button {
|
|
||||||
opacity: 0.5;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-container-card.preview-mode .clip-segment:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
border-color: rgba(0, 0, 0, 0.15);
|
|
||||||
background-color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Segments playback mode styles - minimal functional styling */
|
/* Segments playback mode styles - minimal functional styling */
|
||||||
.segments-playback-mode .tooltip-time-btn {
|
.segments-playback-mode .tooltip-time-btn {
|
||||||
|
|||||||
@ -227,6 +227,43 @@
|
|||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure pause button is properly styled when disabled */
|
||||||
|
.tooltip-action-btn.pause.disabled {
|
||||||
|
color: #9ca3af !important;
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-action-btn.pause.disabled:hover {
|
||||||
|
background-color: #f3f4f6 !important;
|
||||||
|
color: #9ca3af !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure play button is properly styled when disabled */
|
||||||
|
.tooltip-action-btn.play.disabled {
|
||||||
|
color: #9ca3af !important;
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-action-btn.play.disabled:hover {
|
||||||
|
background-color: #f3f4f6 !important;
|
||||||
|
color: #9ca3af !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure time adjustment buttons are properly styled when disabled */
|
||||||
|
.tooltip-time-btn.disabled {
|
||||||
|
opacity: 0.5 !important;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
background-color: #f3f4f6 !important;
|
||||||
|
color: #9ca3af !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-time-btn.disabled:hover {
|
||||||
|
background-color: #f3f4f6 !important;
|
||||||
|
color: #9ca3af !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Additional mobile optimizations */
|
/* Additional mobile optimizations */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.two-row-tooltip {
|
.two-row-tooltip {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user