feat: Improve Visual Distinction Between Trim and Chapters Editors (#1445)

* Update .gitignore

* feat: Improve Visual Distinction Between Trim and Chapters Editors

* fix: Convert timeline header styles to CSS classes

Moved inline styles for timeline headers in chapters and video editors to dedicated CSS classes for better maintainability and consistency.

* Bump version to 7.3.0

Update the VERSION in cms/version.py to 7.3.0 for the new release.

* build assets

* Update segment color schemes in video and chapters editor.

* build assets

* build assets

* fix: Prevent Safari from resetting segments after drag operations

Prevent Safari from resetting segments when loadedmetadata fires multiple times and fix stale state issues in click handlers by using refs instead of closure variables.

* build assets

* Bump version to 7.3.0-beta.3

Update the VERSION string in cms/version.py to reflect the new pre-release version 7.3.0-beta.3.
This commit is contained in:
Yiannis Christodoulou 2025-12-22 11:12:19 +02:00 committed by GitHub
parent aeef8284bf
commit d9b1d6cab1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 398 additions and 254 deletions

1
.gitignore vendored
View File

@ -35,3 +35,4 @@ frontend-tools/video-editor/client/public/videos/sample-video.mp3
frontend-tools/chapters-editor/client/public/videos/sample-video.mp3 frontend-tools/chapters-editor/client/public/videos/sample-video.mp3
static/chapters_editor/videos/sample-video.mp3 static/chapters_editor/videos/sample-video.mp3
static/video_editor/videos/sample-video.mp3 static/video_editor/videos/sample-video.mp3
templates/todo-MS4.md

View File

@ -1 +1 @@
VERSION = "7.2.2" VERSION = "7.3.0-beta.3"

View File

@ -150,6 +150,11 @@ const App = () => {
canRedo={historyPosition < history.length - 1} canRedo={historyPosition < history.length - 1}
/> />
{/* Timeline Header */}
<div className="timeline-header-container">
<h2 className="timeline-header-title">Add Chapters</h2>
</div>
{/* Timeline Controls */} {/* Timeline Controls */}
<TimelineControls <TimelineControls
currentTime={currentTime} currentTime={currentTime}

View File

@ -28,9 +28,9 @@ const ClipSegments = ({ segments, selectedSegmentId }: ClipSegmentsProps) => {
// Generate the same color background for a segment as shown in the timeline // Generate the same color background for a segment as shown in the timeline
const getSegmentColorClass = (index: number) => { const getSegmentColorClass = (index: number) => {
// Return CSS class based on index modulo 8 // Return CSS class based on index modulo 20
// This matches the CSS nth-child selectors in the timeline // This matches the CSS classes for up to 20 segments
return `segment-default-color segment-color-${(index % 8) + 1}`; return `segment-default-color segment-color-${(index % 20) + 1}`;
}; };
// Get selected segment // Get selected segment
@ -65,8 +65,8 @@ const ClipSegments = ({ segments, selectedSegmentId }: ClipSegmentsProps) => {
<div className="segment-actions"> <div className="segment-actions">
<button <button
className="delete-button" className="delete-button"
aria-label="Delete Segment" aria-label="Delete Chapter"
data-tooltip="Delete this segment" data-tooltip="Delete this chapter"
onClick={() => handleDeleteSegment(segment.id)} onClick={() => handleDeleteSegment(segment.id)}
> >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">

View File

@ -177,7 +177,16 @@ const TimelineControls = ({
const [isAutoSaving, setIsAutoSaving] = useState(false); const [isAutoSaving, setIsAutoSaving] = useState(false);
const autoSaveTimerRef = useRef<NodeJS.Timeout | null>(null); const autoSaveTimerRef = useRef<NodeJS.Timeout | null>(null);
const clipSegmentsRef = useRef(clipSegments); const clipSegmentsRef = useRef(clipSegments);
// Track when a drag just ended to prevent Safari from triggering clicks after drag
const dragJustEndedRef = useRef<boolean>(false);
const dragEndTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// Helper function to detect Safari browser
const isSafari = () => {
if (typeof window === 'undefined') return false;
const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera;
return /Safari/.test(userAgent) && !/Chrome/.test(userAgent) && !/Chromium/.test(userAgent);
};
// Keep clipSegmentsRef updated // Keep clipSegmentsRef updated
useEffect(() => { useEffect(() => {
@ -867,6 +876,12 @@ const TimelineControls = ({
logger.debug('Clearing auto-save timer in cleanup:', autoSaveTimerRef.current); logger.debug('Clearing auto-save timer in cleanup:', autoSaveTimerRef.current);
clearTimeout(autoSaveTimerRef.current); clearTimeout(autoSaveTimerRef.current);
} }
// Clear any pending drag end timeout
if (dragEndTimeoutRef.current) {
clearTimeout(dragEndTimeoutRef.current);
dragEndTimeoutRef.current = null;
}
}; };
}, [scheduleAutoSave]); }, [scheduleAutoSave]);
@ -1084,16 +1099,20 @@ const TimelineControls = ({
}; };
// Helper function to calculate available space for a new segment // Helper function to calculate available space for a new segment
const calculateAvailableSpace = (startTime: number): number => { const calculateAvailableSpace = (startTime: number, segmentsOverride?: Segment[]): number => {
// Always return at least 0.1 seconds to ensure tooltip shows // Always return at least 0.1 seconds to ensure tooltip shows
const MIN_SPACE = 0.1; const MIN_SPACE = 0.1;
// Use override segments if provided, otherwise use ref to get latest segments
// This ensures we always have the most up-to-date segments, especially important for Safari
const segmentsToUse = segmentsOverride || clipSegmentsRef.current;
// Determine the amount of available space: // Determine the amount of available space:
// 1. Check remaining space until the end of video // 1. Check remaining space until the end of video
const remainingDuration = Math.max(0, duration - startTime); const remainingDuration = Math.max(0, duration - startTime);
// 2. Find the next segment (if any) // 2. Find the next segment (if any)
const sortedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime); const sortedSegments = [...segmentsToUse].sort((a, b) => a.startTime - b.startTime);
// Find the next and previous segments // Find the next and previous segments
const nextSegment = sortedSegments.find((seg) => seg.startTime > startTime); const nextSegment = sortedSegments.find((seg) => seg.startTime > startTime);
@ -1109,14 +1128,6 @@ const TimelineControls = ({
availableSpace = duration - startTime; availableSpace = duration - startTime;
} }
// Log the space calculation for debugging
logger.debug('Space calculation:', {
position: formatDetailedTime(startTime),
nextSegment: nextSegment ? formatDetailedTime(nextSegment.startTime) : 'none',
prevSegment: prevSegment ? formatDetailedTime(prevSegment.endTime) : 'none',
availableSpace: formatDetailedTime(Math.max(MIN_SPACE, availableSpace)),
});
// Always return at least MIN_SPACE to ensure tooltip shows // Always return at least MIN_SPACE to ensure tooltip shows
return Math.max(MIN_SPACE, availableSpace); return Math.max(MIN_SPACE, availableSpace);
}; };
@ -1125,8 +1136,11 @@ const TimelineControls = ({
const updateTooltipForPosition = (currentPosition: number) => { const updateTooltipForPosition = (currentPosition: number) => {
if (!timelineRef.current) return; if (!timelineRef.current) return;
// Use ref to get latest segments to avoid stale state issues
const currentSegments = clipSegmentsRef.current;
// Find if we're in a segment at the current position with a small tolerance // Find if we're in a segment at the current position with a small tolerance
const segmentAtPosition = clipSegments.find((seg) => { const segmentAtPosition = currentSegments.find((seg) => {
const isWithinSegment = currentPosition >= seg.startTime && currentPosition <= seg.endTime; const isWithinSegment = currentPosition >= seg.startTime && currentPosition <= seg.endTime;
const isVeryCloseToStart = Math.abs(currentPosition - seg.startTime) < 0.001; const isVeryCloseToStart = Math.abs(currentPosition - seg.startTime) < 0.001;
const isVeryCloseToEnd = Math.abs(currentPosition - seg.endTime) < 0.001; const isVeryCloseToEnd = Math.abs(currentPosition - seg.endTime) < 0.001;
@ -1134,7 +1148,7 @@ const TimelineControls = ({
}); });
// Find the next and previous segments // Find the next and previous segments
const sortedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime); const sortedSegments = [...currentSegments].sort((a, b) => a.startTime - b.startTime);
const nextSegment = sortedSegments.find((seg) => seg.startTime > currentPosition); const nextSegment = sortedSegments.find((seg) => seg.startTime > currentPosition);
const prevSegment = [...sortedSegments].reverse().find((seg) => seg.endTime < currentPosition); const prevSegment = [...sortedSegments].reverse().find((seg) => seg.endTime < currentPosition);
@ -1144,21 +1158,13 @@ const TimelineControls = ({
setShowEmptySpaceTooltip(false); setShowEmptySpaceTooltip(false);
} else { } else {
// We're in a cutaway area // We're in a cutaway area
// Calculate available space for new segment // Calculate available space for new segment using current segments
const availableSpace = calculateAvailableSpace(currentPosition); const availableSpace = calculateAvailableSpace(currentPosition, currentSegments);
setAvailableSegmentDuration(availableSpace); setAvailableSegmentDuration(availableSpace);
// Always show empty space tooltip // Always show empty space tooltip
setSelectedSegmentId(null); setSelectedSegmentId(null);
setShowEmptySpaceTooltip(true); setShowEmptySpaceTooltip(true);
// Log position info for debugging
logger.debug('Cutaway position:', {
current: formatDetailedTime(currentPosition),
prevSegmentEnd: prevSegment ? formatDetailedTime(prevSegment.endTime) : 'none',
nextSegmentStart: nextSegment ? formatDetailedTime(nextSegment.startTime) : 'none',
availableSpace: formatDetailedTime(availableSpace),
});
} }
// Update tooltip position // Update tooltip position
@ -1188,6 +1194,12 @@ const TimelineControls = ({
if (!timelineRef.current || !scrollContainerRef.current) return; if (!timelineRef.current || !scrollContainerRef.current) return;
// Safari-specific fix: Ignore clicks that happen immediately after a drag operation
// Safari fires click events after drag ends, which can cause issues with stale state
if (isSafari() && dragJustEndedRef.current) {
return;
}
// If on mobile device and video hasn't been initialized, don't handle timeline clicks // If on mobile device and video hasn't been initialized, don't handle timeline clicks
if (isIOSUninitialized) { if (isIOSUninitialized) {
return; return;
@ -1195,7 +1207,6 @@ const TimelineControls = ({
// Check if video is globally playing before the click // Check if video is globally playing before the click
const wasPlaying = videoRef.current && !videoRef.current.paused; const wasPlaying = videoRef.current && !videoRef.current.paused;
logger.debug('Video was playing before timeline click:', wasPlaying);
// Reset continuation flag when clicking on timeline - ensures proper boundary detection // Reset continuation flag when clicking on timeline - ensures proper boundary detection
setContinuePastBoundary(false); setContinuePastBoundary(false);
@ -1216,14 +1227,6 @@ const TimelineControls = ({
const newTime = position * duration; const newTime = position * duration;
// Log the position for debugging
logger.debug(
'Timeline clicked at:',
formatDetailedTime(newTime),
'distance from end:',
formatDetailedTime(duration - newTime)
);
// Store position globally for iOS Safari (this is critical for first-time visits) // Store position globally for iOS Safari (this is critical for first-time visits)
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.lastSeekedPosition = newTime; window.lastSeekedPosition = newTime;
@ -1236,8 +1239,12 @@ const TimelineControls = ({
setClickedTime(newTime); setClickedTime(newTime);
setDisplayTime(newTime); setDisplayTime(newTime);
// Use ref to get latest segments to avoid stale state issues, especially in Safari
// Safari can fire click events immediately after drag before React re-renders
const currentSegments = clipSegmentsRef.current;
// Find if we clicked in a segment with a small tolerance for boundaries // Find if we clicked in a segment with a small tolerance for boundaries
const segmentAtClickedTime = clipSegments.find((seg) => { const segmentAtClickedTime = currentSegments.find((seg) => {
// Standard check for being inside a segment // Standard check for being inside a segment
const isInside = newTime >= seg.startTime && newTime <= seg.endTime; const isInside = newTime >= seg.startTime && newTime <= seg.endTime;
// Additional checks for being exactly at the start or end boundary (with small tolerance) // Additional checks for being exactly at the start or end boundary (with small tolerance)
@ -1258,7 +1265,7 @@ const TimelineControls = ({
if (isPlayingSegments && wasPlaying) { if (isPlayingSegments && wasPlaying) {
// Update the current segment index if we clicked into a segment // Update the current segment index if we clicked into a segment
if (segmentAtClickedTime) { if (segmentAtClickedTime) {
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime); const orderedSegments = [...currentSegments].sort((a, b) => a.startTime - b.startTime);
const targetSegmentIndex = orderedSegments.findIndex((seg) => seg.id === segmentAtClickedTime.id); const targetSegmentIndex = orderedSegments.findIndex((seg) => seg.id === segmentAtClickedTime.id);
if (targetSegmentIndex !== -1) { if (targetSegmentIndex !== -1) {
@ -1311,8 +1318,9 @@ const TimelineControls = ({
// We're in a cutaway area - always show tooltip // We're in a cutaway area - always show tooltip
setSelectedSegmentId(null); setSelectedSegmentId(null);
// Calculate the available space for a new segment // Calculate the available space for a new segment using current segments from ref
const availableSpace = calculateAvailableSpace(newTime); // This ensures we use the latest segments even if React hasn't re-rendered yet
const availableSpace = calculateAvailableSpace(newTime, currentSegments);
setAvailableSegmentDuration(availableSpace); setAvailableSegmentDuration(availableSpace);
// Calculate and set tooltip position correctly for zoomed timeline // Calculate and set tooltip position correctly for zoomed timeline
@ -1334,18 +1342,6 @@ const TimelineControls = ({
// Always show the empty space tooltip in cutaway areas // Always show the empty space tooltip in cutaway areas
setShowEmptySpaceTooltip(true); setShowEmptySpaceTooltip(true);
// Log the cutaway area details
const sortedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
const prevSegment = [...sortedSegments].reverse().find((seg) => seg.endTime < newTime);
const nextSegment = sortedSegments.find((seg) => seg.startTime > newTime);
logger.debug('Clicked in cutaway area:', {
position: formatDetailedTime(newTime),
availableSpace: formatDetailedTime(availableSpace),
prevSegmentEnd: prevSegment ? formatDetailedTime(prevSegment.endTime) : 'none',
nextSegmentStart: nextSegment ? formatDetailedTime(nextSegment.startTime) : 'none',
});
} }
} }
}; };
@ -1498,6 +1494,10 @@ const TimelineControls = ({
return seg; return seg;
}); });
// Update the ref immediately during drag to ensure we always have latest segments
// This is critical for Safari which may fire events before React re-renders
clipSegmentsRef.current = updatedSegments;
// Create a custom event to update the segments WITHOUT recording in history during drag // Create a custom event to update the segments WITHOUT recording in history during drag
const updateEvent = new CustomEvent('update-segments', { const updateEvent = new CustomEvent('update-segments', {
detail: { detail: {
@ -1582,6 +1582,26 @@ const TimelineControls = ({
return seg; return seg;
}); });
// CRITICAL: Update the ref immediately with the new segments
// This ensures that if Safari fires a click event before React re-renders,
// the click handler will use the updated segments instead of stale ones
clipSegmentsRef.current = finalSegments;
// Safari-specific fix: Set flag to ignore clicks immediately after drag
// Safari fires click events after drag ends, which can interfere with state updates
if (isSafari()) {
dragJustEndedRef.current = true;
// Clear the flag after a delay to allow React to re-render with updated segments
// Increased timeout to ensure state has propagated
if (dragEndTimeoutRef.current) {
clearTimeout(dragEndTimeoutRef.current);
}
dragEndTimeoutRef.current = setTimeout(() => {
dragJustEndedRef.current = false;
dragEndTimeoutRef.current = null;
}, 200); // 200ms to ensure React has processed the state update and re-rendered
}
// Now we can create a history record for the complete drag operation // Now we can create a history record for the complete drag operation
const actionType = isLeft ? 'adjust_segment_start' : 'adjust_segment_end'; const actionType = isLeft ? 'adjust_segment_start' : 'adjust_segment_end';
document.dispatchEvent( document.dispatchEvent(
@ -1594,6 +1614,13 @@ const TimelineControls = ({
}) })
); );
// Dispatch segment-drag-end event for other listeners
document.dispatchEvent(
new CustomEvent('segment-drag-end', {
detail: { segmentId },
})
);
// After drag is complete, do a final check to see if playhead is inside the segment // After drag is complete, do a final check to see if playhead is inside the segment
if (selectedSegmentId === segmentId && videoRef.current) { if (selectedSegmentId === segmentId && videoRef.current) {
const currentTime = videoRef.current.currentTime; const currentTime = videoRef.current.currentTime;
@ -3943,9 +3970,7 @@ const TimelineControls = ({
<button <button
onClick={() => setShowSaveChaptersModal(true)} onClick={() => setShowSaveChaptersModal(true)}
className="save-chapters-button" className="save-chapters-button"
data-tooltip={clipSegments.length === 0 {...(clipSegments.length === 0 && { 'data-tooltip': 'Clear all chapters' })}
? "Clear all chapters"
: "Save chapters"}
> >
{clipSegments.length === 0 {clipSegments.length === 0
? 'Clear Chapters' ? 'Clear Chapters'

View File

@ -60,6 +60,9 @@ const useVideoChapters = () => {
const [duration, setDuration] = useState(0); const [duration, setDuration] = useState(0);
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const [isMuted, setIsMuted] = useState(false); const [isMuted, setIsMuted] = useState(false);
// Track if editor has been initialized to prevent re-initialization on Safari metadata events
const isInitializedRef = useRef<boolean>(false);
// Timeline state // Timeline state
const [trimStart, setTrimStart] = useState(0); const [trimStart, setTrimStart] = useState(0);
@ -108,11 +111,7 @@ const useVideoChapters = () => {
// Detect Safari browser // Detect Safari browser
const isSafari = () => { const isSafari = () => {
const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera; const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera;
const isSafariBrowser = /Safari/.test(userAgent) && !/Chrome/.test(userAgent) && !/Chromium/.test(userAgent); return /Safari/.test(userAgent) && !/Chrome/.test(userAgent) && !/Chromium/.test(userAgent);
if (isSafariBrowser) {
logger.debug('Safari browser detected, enabling audio support fallbacks');
}
return isSafariBrowser;
}; };
// Initialize video event listeners // Initialize video event listeners
@ -121,7 +120,15 @@ const useVideoChapters = () => {
if (!video) return; if (!video) return;
const handleLoadedMetadata = () => { const handleLoadedMetadata = () => {
logger.debug('Video loadedmetadata event fired, duration:', video.duration); // CRITICAL: Prevent re-initialization if editor has already been initialized
// Safari fires loadedmetadata multiple times, which was resetting segments
if (isInitializedRef.current) {
// Still update duration and trimEnd in case they changed
setDuration(video.duration);
setTrimEnd(video.duration);
return;
}
setDuration(video.duration); setDuration(video.duration);
setTrimEnd(video.duration); setTrimEnd(video.duration);
@ -173,7 +180,7 @@ const useVideoChapters = () => {
setHistory([initialState]); setHistory([initialState]);
setHistoryPosition(0); setHistoryPosition(0);
setClipSegments(initialSegments); setClipSegments(initialSegments);
logger.debug('Editor initialized with segments:', initialSegments.length); isInitializedRef.current = true; // Mark as initialized
}; };
initializeEditor(); initializeEditor();
@ -181,20 +188,18 @@ const useVideoChapters = () => {
// Safari-specific fallback for audio files // Safari-specific fallback for audio files
const handleCanPlay = () => { const handleCanPlay = () => {
logger.debug('Video canplay event fired');
// If loadedmetadata hasn't fired yet but we have duration, trigger initialization // If loadedmetadata hasn't fired yet but we have duration, trigger initialization
if (video.duration && duration === 0) { // Also check if already initialized to prevent re-initialization
logger.debug('Safari fallback: Using canplay event to initialize'); if (video.duration && duration === 0 && !isInitializedRef.current) {
handleLoadedMetadata(); handleLoadedMetadata();
} }
}; };
// Additional Safari fallback for audio files // Additional Safari fallback for audio files
const handleLoadedData = () => { const handleLoadedData = () => {
logger.debug('Video loadeddata event fired');
// If we still don't have duration, try again // If we still don't have duration, try again
if (video.duration && duration === 0) { // Also check if already initialized to prevent re-initialization
logger.debug('Safari fallback: Using loadeddata event to initialize'); if (video.duration && duration === 0 && !isInitializedRef.current) {
handleLoadedMetadata(); handleLoadedMetadata();
} }
}; };
@ -226,14 +231,12 @@ const useVideoChapters = () => {
// Safari-specific fallback event listeners for audio files // Safari-specific fallback event listeners for audio files
if (isSafari()) { if (isSafari()) {
logger.debug('Adding Safari-specific event listeners for audio support');
video.addEventListener('canplay', handleCanPlay); video.addEventListener('canplay', handleCanPlay);
video.addEventListener('loadeddata', handleLoadedData); video.addEventListener('loadeddata', handleLoadedData);
// Additional timeout fallback for Safari audio files // Additional timeout fallback for Safari audio files
const safariTimeout = setTimeout(() => { const safariTimeout = setTimeout(() => {
if (video.duration && duration === 0) { if (video.duration && duration === 0 && !isInitializedRef.current) {
logger.debug('Safari timeout fallback: Force initializing editor');
handleLoadedMetadata(); handleLoadedMetadata();
} }
}, 1000); }, 1000);

View File

@ -82,27 +82,24 @@
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 500; font-weight: 500;
color: var(--foreground, #333); color: var(--foreground, #333);
margin: 0; margin-bottom: 0.75rem;
} }
.save-chapters-button { .save-chapters-button {
display: flex; color: #ffffff;
align-items: center; background: #059669;
gap: 0.5rem; border-radius: 0.25rem;
padding: 0.5rem 1rem; font-size: 0.75rem;
background-color: #3b82f6; padding: 0.25rem 0.5rem;
color: white;
border: none;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; border: none;
white-space: nowrap;
transition: background-color 0.2s;
min-width: fit-content;
&:hover { &:hover {
background-color: #2563eb; background-color: #059669;
transform: translateY(-1px); box-shadow: 0 4px 6px -1px rgba(5, 150, 105, 0.3);
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
} }
&.has-changes { &.has-changes {
@ -205,9 +202,9 @@
} }
&.selected { &.selected {
border-color: #3b82f6; border-color: #059669;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.1);
background-color: rgba(59, 130, 246, 0.05); background-color: rgba(5, 150, 105, 0.05);
} }
} }
@ -287,29 +284,68 @@
color: rgba(51, 51, 51, 0.7); color: rgba(51, 51, 51, 0.7);
} }
/* Generate 20 shades of #059669 (rgb(5, 150, 105)) */
/* Base color: #059669 = rgb(5, 150, 105) */
/* Creating variations from lighter to darker */
.segment-color-1 { .segment-color-1 {
background-color: rgba(59, 130, 246, 0.15); background-color: rgba(167, 243, 208, 0.2);
} }
.segment-color-2 { .segment-color-2 {
background-color: rgba(16, 185, 129, 0.15); background-color: rgba(134, 239, 172, 0.2);
} }
.segment-color-3 { .segment-color-3 {
background-color: rgba(245, 158, 11, 0.15); background-color: rgba(101, 235, 136, 0.2);
} }
.segment-color-4 { .segment-color-4 {
background-color: rgba(239, 68, 68, 0.15); background-color: rgba(68, 231, 100, 0.2);
} }
.segment-color-5 { .segment-color-5 {
background-color: rgba(139, 92, 246, 0.15); background-color: rgba(35, 227, 64, 0.2);
} }
.segment-color-6 { .segment-color-6 {
background-color: rgba(236, 72, 153, 0.15); background-color: rgba(20, 207, 54, 0.2);
} }
.segment-color-7 { .segment-color-7 {
background-color: rgba(6, 182, 212, 0.15); background-color: rgba(15, 187, 48, 0.2);
} }
.segment-color-8 { .segment-color-8 {
background-color: rgba(250, 204, 21, 0.15); background-color: rgba(10, 167, 42, 0.2);
}
.segment-color-9 {
background-color: rgba(5, 150, 105, 0.2);
}
.segment-color-10 {
background-color: rgba(4, 135, 95, 0.2);
}
.segment-color-11 {
background-color: rgba(3, 120, 85, 0.2);
}
.segment-color-12 {
background-color: rgba(2, 105, 75, 0.2);
}
.segment-color-13 {
background-color: rgba(2, 90, 65, 0.2);
}
.segment-color-14 {
background-color: rgba(1, 75, 55, 0.2);
}
.segment-color-15 {
background-color: rgba(1, 66, 48, 0.2);
}
.segment-color-16 {
background-color: rgba(1, 57, 41, 0.2);
}
.segment-color-17 {
background-color: rgba(1, 48, 34, 0.2);
}
.segment-color-18 {
background-color: rgba(0, 39, 27, 0.2);
}
.segment-color-19 {
background-color: rgba(0, 30, 20, 0.2);
}
.segment-color-20 {
background-color: rgba(0, 21, 13, 0.2);
} }
/* Responsive styles */ /* Responsive styles */

View File

@ -31,7 +31,7 @@
.ios-notification-icon { .ios-notification-icon {
flex-shrink: 0; flex-shrink: 0;
color: #0066cc; color: #059669;
margin-right: 15px; margin-right: 15px;
margin-top: 3px; margin-top: 3px;
} }
@ -96,7 +96,7 @@
} }
.ios-desktop-mode-btn { .ios-desktop-mode-btn {
background-color: #0066cc; background-color: #059669;
color: white; color: white;
border: none; border: none;
border-radius: 8px; border-radius: 8px;

View File

@ -92,12 +92,12 @@
} }
.modal-button-primary { .modal-button-primary {
background-color: #0066cc; background-color: #059669;
color: white; color: white;
} }
.modal-button-primary:hover { .modal-button-primary:hover {
background-color: #0055aa; background-color: #059669;
} }
.modal-button-secondary { .modal-button-secondary {
@ -138,7 +138,7 @@
.spinner { .spinner {
border: 4px solid rgba(0, 0, 0, 0.1); border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%; border-radius: 50%;
border-top: 4px solid #0066cc; border-top: 4px solid #059669;
width: 30px; width: 30px;
height: 30px; height: 30px;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
@ -224,7 +224,7 @@
padding: 12px 16px; padding: 12px 16px;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
background-color: #0066cc; background-color: #059669;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
@ -258,12 +258,12 @@
margin: 0 auto; margin: 0 auto;
width: auto; width: auto;
min-width: 220px; min-width: 220px;
background-color: #0066cc; background-color: #059669;
color: white; color: white;
} }
.centered-choice:hover { .centered-choice:hover {
background-color: #0055aa; background-color: #059669;
} }
@media (max-width: 480px) { @media (max-width: 480px) {
@ -300,7 +300,7 @@
.countdown { .countdown {
font-weight: bold; font-weight: bold;
color: #0066cc; color: #059669;
font-size: 1.1rem; font-size: 1.1rem;
} }
} }

View File

@ -1,4 +1,16 @@
#chapters-editor-root { #chapters-editor-root {
.timeline-header-container {
margin-left: 1rem;
margin-top: -0.5rem;
}
.timeline-header-title {
font-size: 1.125rem;
font-weight: 600;
color: #059669;
margin: 0;
}
.timeline-container-card { .timeline-container-card {
background-color: white; background-color: white;
border-radius: 0.5rem; border-radius: 0.5rem;
@ -11,6 +23,8 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding-bottom: 0.5rem;
border-bottom: 2px solid rgba(16, 185, 129, 0.2);
} }
.timeline-title { .timeline-title {
@ -20,7 +34,7 @@
} }
.timeline-title-text { .timeline-title-text {
font-weight: 700; font-size: 0.875rem;
} }
.current-time { .current-time {
@ -48,10 +62,11 @@
.timeline-container { .timeline-container {
position: relative; position: relative;
min-width: 100%; min-width: 100%;
background-color: #fafbfc; background-color: #e2ede4;
height: 70px; height: 70px;
border-radius: 0.25rem; border-radius: 0.25rem;
overflow: visible !important; overflow: visible !important;
border: 1px solid rgba(16, 185, 129, 0.2);
} }
.timeline-marker { .timeline-marker {
@ -194,7 +209,7 @@
left: 0; left: 0;
right: 0; right: 0;
padding: 0.4rem; padding: 0.4rem;
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(16, 185, 129, 0.6);
color: white; color: white;
opacity: 1; opacity: 1;
transition: background-color 0.2s; transition: background-color 0.2s;
@ -202,15 +217,15 @@
} }
.clip-segment:hover .clip-segment-info { .clip-segment:hover .clip-segment-info {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(16, 185, 129, 0.7);
} }
.clip-segment.selected .clip-segment-info { .clip-segment.selected .clip-segment-info {
background-color: rgba(59, 130, 246, 0.5); background-color: rgba(5, 150, 105, 0.8);
} }
.clip-segment.selected:hover .clip-segment-info { .clip-segment.selected:hover .clip-segment-info {
background-color: rgba(59, 130, 246, 0.4); background-color: rgba(5, 150, 105, 0.75);
} }
.clip-segment-name { .clip-segment-name {
@ -540,7 +555,7 @@
.save-copy-button, .save-copy-button,
.save-segments-button { .save-segments-button {
color: #ffffff; color: #ffffff;
background: #0066cc; background: #059669;
border-radius: 0.25rem; border-radius: 0.25rem;
font-size: 0.75rem; font-size: 0.75rem;
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
@ -713,7 +728,7 @@
height: 50px; height: 50px;
border: 5px solid rgba(0, 0, 0, 0.1); border: 5px solid rgba(0, 0, 0, 0.1);
border-radius: 50%; border-radius: 50%;
border-top-color: #0066cc; border-top-color: #059669;
animation: spin 1s ease-in-out infinite; animation: spin 1s ease-in-out infinite;
} }
@ -753,7 +768,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0.75rem 1.25rem; padding: 0.75rem 1.25rem;
background-color: #0066cc; background-color: #059669;
color: white; color: white;
border-radius: 4px; border-radius: 4px;
text-decoration: none; text-decoration: none;
@ -766,7 +781,7 @@
} }
.modal-choice-button:hover { .modal-choice-button:hover {
background-color: #0056b3; background-color:rgb(7, 119, 84);
} }
.modal-choice-button svg { .modal-choice-button svg {
@ -941,7 +956,6 @@
.save-chapters-button:hover { .save-chapters-button:hover {
background-color: #2563eb; background-color: #2563eb;
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3); box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
} }

View File

@ -309,6 +309,11 @@ const App = () => {
canRedo={historyPosition < history.length - 1} canRedo={historyPosition < history.length - 1}
/> />
{/* Timeline Header */}
<div className="timeline-header-container">
<h2 className="timeline-header-title">Trim or Split</h2>
</div>
{/* Timeline Controls */} {/* Timeline Controls */}
<TimelineControls <TimelineControls
currentTime={currentTime} currentTime={currentTime}

View File

@ -28,9 +28,9 @@ const ClipSegments = ({ segments }: ClipSegmentsProps) => {
// Generate the same color background for a segment as shown in the timeline // Generate the same color background for a segment as shown in the timeline
const getSegmentColorClass = (index: number) => { const getSegmentColorClass = (index: number) => {
// Return CSS class based on index modulo 8 // Return CSS class based on index modulo 20
// This matches the CSS nth-child selectors in the timeline // This matches the CSS classes for up to 20 segments
return `segment-default-color segment-color-${(index % 8) + 1}`; return `segment-default-color segment-color-${(index % 20) + 1}`;
}; };
return ( return (

View File

@ -99,6 +99,7 @@
} }
.segment-thumbnail { .segment-thumbnail {
display: none;
width: 4rem; width: 4rem;
height: 2.25rem; height: 2.25rem;
background-size: cover; background-size: cover;
@ -129,7 +130,7 @@
margin-top: 0.25rem; margin-top: 0.25rem;
display: inline-block; display: inline-block;
background-color: #f3f4f6; background-color: #f3f4f6;
padding: 0 0.5rem; padding: 0;
border-radius: 0.25rem; border-radius: 0.25rem;
color: black; color: black;
} }
@ -169,28 +170,67 @@
color: rgba(51, 51, 51, 0.7); color: rgba(51, 51, 51, 0.7);
} }
/* Generate 20 shades of #2563eb (rgb(37, 99, 235)) */
/* Base color: #2563eb = rgb(37, 99, 235) */
/* Creating variations from lighter to darker */
.segment-color-1 { .segment-color-1 {
background-color: rgba(59, 130, 246, 0.15); background-color: rgba(147, 179, 247, 0.2);
} }
.segment-color-2 { .segment-color-2 {
background-color: rgba(16, 185, 129, 0.15); background-color: rgba(129, 161, 243, 0.2);
} }
.segment-color-3 { .segment-color-3 {
background-color: rgba(245, 158, 11, 0.15); background-color: rgba(111, 143, 239, 0.2);
} }
.segment-color-4 { .segment-color-4 {
background-color: rgba(239, 68, 68, 0.15); background-color: rgba(93, 125, 237, 0.2);
} }
.segment-color-5 { .segment-color-5 {
background-color: rgba(139, 92, 246, 0.15); background-color: rgba(75, 107, 235, 0.2);
} }
.segment-color-6 { .segment-color-6 {
background-color: rgba(236, 72, 153, 0.15); background-color: rgba(65, 99, 235, 0.2);
} }
.segment-color-7 { .segment-color-7 {
background-color: rgba(6, 182, 212, 0.15); background-color: rgba(55, 91, 235, 0.2);
} }
.segment-color-8 { .segment-color-8 {
background-color: rgba(250, 204, 21, 0.15); background-color: rgba(45, 83, 235, 0.2);
}
.segment-color-9 {
background-color: rgba(37, 99, 235, 0.2);
}
.segment-color-10 {
background-color: rgba(33, 89, 215, 0.2);
}
.segment-color-11 {
background-color: rgba(29, 79, 195, 0.2);
}
.segment-color-12 {
background-color: rgba(25, 69, 175, 0.2);
}
.segment-color-13 {
background-color: rgba(21, 59, 155, 0.2);
}
.segment-color-14 {
background-color: rgba(17, 49, 135, 0.2);
}
.segment-color-15 {
background-color: rgba(15, 43, 119, 0.2);
}
.segment-color-16 {
background-color: rgba(13, 37, 103, 0.2);
}
.segment-color-17 {
background-color: rgba(11, 31, 87, 0.2);
}
.segment-color-18 {
background-color: rgba(9, 25, 71, 0.2);
}
.segment-color-19 {
background-color: rgba(7, 19, 55, 0.2);
}
.segment-color-20 {
background-color: rgba(5, 13, 39, 0.2);
} }
} }

View File

@ -1,4 +1,16 @@
#video-editor-trim-root { #video-editor-trim-root {
.timeline-header-container {
margin-left: 1rem;
margin-top: -0.5rem;
}
.timeline-header-title {
font-size: 1.125rem;
font-weight: 600;
color: #2563eb;
margin: 0;
}
.timeline-container-card { .timeline-container-card {
background-color: white; background-color: white;
border-radius: 0.5rem; border-radius: 0.5rem;
@ -11,6 +23,8 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding-bottom: 0.5rem;
border-bottom: 2px solid rgba(59, 130, 246, 0.2);
} }
.timeline-title { .timeline-title {
@ -20,7 +34,7 @@
} }
.timeline-title-text { .timeline-title-text {
font-weight: 700; font-size: 0.875rem;
} }
.current-time { .current-time {
@ -48,10 +62,11 @@
.timeline-container { .timeline-container {
position: relative; position: relative;
min-width: 100%; min-width: 100%;
background-color: #fafbfc; background-color: #eff6ff;
height: 70px; height: 70px;
border-radius: 0.25rem; border-radius: 0.25rem;
overflow: visible !important; overflow: visible !important;
border: 1px solid rgba(59, 130, 246, 0.2);
} }
.timeline-marker { .timeline-marker {
@ -194,7 +209,7 @@
left: 0; left: 0;
right: 0; right: 0;
padding: 0.4rem; padding: 0.4rem;
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(59, 130, 246, 0.6);
color: white; color: white;
opacity: 1; opacity: 1;
transition: background-color 0.2s; transition: background-color 0.2s;
@ -202,15 +217,15 @@
} }
.clip-segment:hover .clip-segment-info { .clip-segment:hover .clip-segment-info {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(59, 130, 246, 0.7);
} }
.clip-segment.selected .clip-segment-info { .clip-segment.selected .clip-segment-info {
background-color: rgba(59, 130, 246, 0.5); background-color: rgba(37, 99, 235, 0.8);
} }
.clip-segment.selected:hover .clip-segment-info { .clip-segment.selected:hover .clip-segment-info {
background-color: rgba(59, 130, 246, 0.4); background-color: rgba(37, 99, 235, 0.75);
} }
.clip-segment-name { .clip-segment-name {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(){"use strict";var n,e={4256:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=594,function(){var n={594:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(4256)}));o=t.O(o)}(); !function(){"use strict";var n,e={4256:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=594,function(){var n={594:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(4256)});o=t.O(o)}();

View File

@ -1 +1 @@
!function(){"use strict";var n,e={5879:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=543,function(){var n={543:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(5879)}));o=t.O(o)}(); !function(){"use strict";var n,e={5879:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=543,function(){var n={543:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(5879)});o=t.O(o)}();

View File

@ -1 +1 @@
!function(){"use strict";var n,e={1684:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every((function(n){return t.O[n](r[c])}))?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=152,function(){var n={152:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some((function(e){return 0!==n[e]}))){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],(function(){return t(1684)}));o=t.O(o)}(); !function(){"use strict";var n,e={1684:function(n,e,r){(0,r(2985).C)()}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}t.m=e,n=[],t.O=function(e,r,o,i){if(!r){var u=1/0;for(l=0;l<n.length;l++){r=n[l][0],o=n[l][1],i=n[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&i||u>=i)&&Object.keys(t.O).every(function(n){return t.O[n](r[c])})?r.splice(c--,1):(f=!1,i<u&&(u=i));if(f){n.splice(l--,1);var a=o();void 0!==a&&(e=a)}}return e}i=i||0;for(var l=n.length;l>0&&n[l-1][2]>i;l--)n[l]=n[l-1];n[l]=[r,o,i]},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,{a:e}),e},t.d=function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(n){if("object"==typeof window)return window}}(),t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.j=152,function(){var n={152:0};t.O.j=function(e){return 0===n[e]};var e=function(e,r){var o,i,u=r[0],f=r[1],c=r[2],a=0;if(u.some(function(e){return 0!==n[e]})){for(o in f)t.o(f,o)&&(t.m[o]=f[o]);if(c)var l=c(t)}for(e&&e(r);a<u.length;a++)i=u[a],t.o(n,i)&&n[i]&&n[i][0](),n[i]=0;return t.O(l)},r=self.webpackChunkmediacms_frontend=self.webpackChunkmediacms_frontend||[];r.forEach(e.bind(null,0)),r.push=e.bind(null,r.push.bind(r))}();var o=t.O(void 0,[276],function(){return t(1684)});o=t.O(o)}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long