|
|
|
@ -9,6 +9,7 @@ import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
|
|
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
|
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
|
|
import { CSS } from '@dnd-kit/utilities';
|
|
|
|
import { CSS } from '@dnd-kit/utilities';
|
|
|
|
import invariant from 'tiny-invariant';
|
|
|
|
import invariant from 'tiny-invariant';
|
|
|
|
|
|
|
|
import prettyBytes from 'pretty-bytes';
|
|
|
|
|
|
|
|
|
|
|
|
import useContextMenu from './hooks/useContextMenu';
|
|
|
|
import useContextMenu from './hooks/useContextMenu';
|
|
|
|
import useUserSettings from './hooks/useUserSettings';
|
|
|
|
import useUserSettings from './hooks/useUserSettings';
|
|
|
|
@ -62,6 +63,7 @@ const Segment = memo(({
|
|
|
|
onExtractSelectedSegmentsFramesAsImages,
|
|
|
|
onExtractSelectedSegmentsFramesAsImages,
|
|
|
|
onInvertSelectedSegments,
|
|
|
|
onInvertSelectedSegments,
|
|
|
|
onDuplicateSegmentClick,
|
|
|
|
onDuplicateSegmentClick,
|
|
|
|
|
|
|
|
getSegEstimatedSize,
|
|
|
|
}: {
|
|
|
|
}: {
|
|
|
|
seg: StateSegment | InverseCutSegment,
|
|
|
|
seg: StateSegment | InverseCutSegment,
|
|
|
|
index: number,
|
|
|
|
index: number,
|
|
|
|
@ -93,6 +95,7 @@ const Segment = memo(({
|
|
|
|
onExtractSelectedSegmentsFramesAsImages: () => void,
|
|
|
|
onExtractSelectedSegmentsFramesAsImages: () => void,
|
|
|
|
onInvertSelectedSegments: UseSegments['invertSelectedSegments'],
|
|
|
|
onInvertSelectedSegments: UseSegments['invertSelectedSegments'],
|
|
|
|
onDuplicateSegmentClick: UseSegments['duplicateSegment'],
|
|
|
|
onDuplicateSegmentClick: UseSegments['duplicateSegment'],
|
|
|
|
|
|
|
|
getSegEstimatedSize: UseSegments['getSegEstimatedSize'],
|
|
|
|
}) => {
|
|
|
|
}) => {
|
|
|
|
const { invertCutSegments, darkMode } = useUserSettings();
|
|
|
|
const { invertCutSegments, darkMode } = useUserSettings();
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const { t } = useTranslation();
|
|
|
|
@ -149,6 +152,7 @@ const Segment = memo(({
|
|
|
|
useContextMenu(ref, contextMenuTemplate);
|
|
|
|
useContextMenu(ref, contextMenuTemplate);
|
|
|
|
|
|
|
|
|
|
|
|
const duration = useMemo(() => (seg.end == null ? undefined : seg.end - seg.start), [seg]);
|
|
|
|
const duration = useMemo(() => (seg.end == null ? undefined : seg.end - seg.start), [seg]);
|
|
|
|
|
|
|
|
const estimatedSize = useMemo(() => getSegEstimatedSize(seg), [getSegEstimatedSize, seg]);
|
|
|
|
|
|
|
|
|
|
|
|
const timeStr = useMemo(() => (
|
|
|
|
const timeStr = useMemo(() => (
|
|
|
|
seg.end == null
|
|
|
|
seg.end == null
|
|
|
|
@ -249,25 +253,32 @@ const Segment = memo(({
|
|
|
|
{...sortable.listeners}
|
|
|
|
{...sortable.listeners}
|
|
|
|
role="button"
|
|
|
|
role="button"
|
|
|
|
tabIndex={-1}
|
|
|
|
tabIndex={-1}
|
|
|
|
style={{ cursor, color: 'var(--gray-12)', marginBottom: duration != null ? 3 : undefined, display: 'flex', alignItems: 'center', height: 16 }}
|
|
|
|
style={{ cursor, color: 'var(--gray-12)', marginBottom: duration != null ? '.1em' : undefined, display: 'flex', alignItems: 'center', height: '1em' }}
|
|
|
|
onClick={handleDraggableClick}
|
|
|
|
onClick={handleDraggableClick}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{renderNumber()}
|
|
|
|
{renderNumber()}
|
|
|
|
<span style={{ cursor, fontSize: Math.min(310 / timeStr.length, 12), whiteSpace: 'nowrap' }}>{timeStr}</span>
|
|
|
|
<span style={{ cursor, fontSize: `${Math.min(1, 26 / timeStr.length) * 0.75}em`, whiteSpace: 'nowrap' }}>{timeStr}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{'name' in seg && seg.name && <span style={{ fontSize: 12, color: primaryTextColor, marginRight: '.3em' }}>{seg.name}</span>}
|
|
|
|
{'name' in seg && seg.name && <span style={{ fontSize: '.75em', color: primaryTextColor, marginRight: '.3em' }}>{seg.name}</span>}
|
|
|
|
{Object.entries(tags).map(([name, value]) => (
|
|
|
|
{Object.entries(tags).map(([name, value]) => (
|
|
|
|
<span style={{ fontSize: 11, backgroundColor: 'var(--gray-5)', color: 'var(--gray-12)', borderRadius: '.4em', padding: '0 .2em', marginRight: '.1em' }} key={name}>{name}:<b>{value}</b></span>
|
|
|
|
<span style={{ fontSize: '.7em', backgroundColor: 'var(--gray-5)', color: 'var(--gray-12)', borderRadius: '.4em', padding: '0 .2em', marginRight: '.1em' }} key={name}>{name}:<b>{value}</b></span>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
|
|
{duration != null && (
|
|
|
|
{duration != null && (
|
|
|
|
<>
|
|
|
|
<>
|
|
|
|
<div style={{ fontSize: 13 }}>
|
|
|
|
<div style={{ fontSize: '.75em' }}>
|
|
|
|
{t('Duration')} {formatTimecode({ seconds: duration, shorten: true })}
|
|
|
|
{t('Duration')} {formatTimecode({ seconds: duration, shorten: true })}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div style={{ fontSize: 12 }}>
|
|
|
|
<div style={{ fontSize: '.75em' }}>
|
|
|
|
<Trans>{{ durationMsFormatted: Math.floor(duration * 1000) }} ms, {{ frameCount: (duration && getFrameCount(duration)) ?? '?' }} frames</Trans>
|
|
|
|
<Trans>{{ durationMsFormatted: Math.floor(duration * 1000) }} ms</Trans>
|
|
|
|
|
|
|
|
<span>, <Trans>{{ frameCount: (duration && getFrameCount(duration)) ?? '?' }} frames</Trans></span>
|
|
|
|
|
|
|
|
{estimatedSize != null && (
|
|
|
|
|
|
|
|
<span style={{ fontSize: '.9em' }}>
|
|
|
|
|
|
|
|
, ~{prettyBytes(estimatedSize, { space: false, maximumFractionDigits: 1, minimumFractionDigits: 0 })}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
@ -321,6 +332,7 @@ function SegmentList({
|
|
|
|
setEditingSegmentTags,
|
|
|
|
setEditingSegmentTags,
|
|
|
|
setEditingSegmentTagsSegmentIndex,
|
|
|
|
setEditingSegmentTagsSegmentIndex,
|
|
|
|
onEditSegmentTags,
|
|
|
|
onEditSegmentTags,
|
|
|
|
|
|
|
|
getSegEstimatedSize,
|
|
|
|
}: {
|
|
|
|
}: {
|
|
|
|
width: number,
|
|
|
|
width: number,
|
|
|
|
formatTimecode: FormatTimecode,
|
|
|
|
formatTimecode: FormatTimecode,
|
|
|
|
@ -361,6 +373,7 @@ function SegmentList({
|
|
|
|
setEditingSegmentTags: Dispatch<SetStateAction<SegmentTags | undefined>>,
|
|
|
|
setEditingSegmentTags: Dispatch<SetStateAction<SegmentTags | undefined>>,
|
|
|
|
setEditingSegmentTagsSegmentIndex: Dispatch<SetStateAction<number | undefined>>,
|
|
|
|
setEditingSegmentTagsSegmentIndex: Dispatch<SetStateAction<number | undefined>>,
|
|
|
|
onEditSegmentTags: (index: number) => void,
|
|
|
|
onEditSegmentTags: (index: number) => void,
|
|
|
|
|
|
|
|
getSegEstimatedSize: UseSegments['getSegEstimatedSize'],
|
|
|
|
}) {
|
|
|
|
}) {
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const { getSegColor, nextSegColorIndex } = useSegColors();
|
|
|
|
const { getSegColor, nextSegColorIndex } = useSegColors();
|
|
|
|
@ -474,7 +487,7 @@ function SegmentList({
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div style={{ padding: '5px 10px', boxSizing: 'border-box', borderBottom: '1px solid var(--gray-6)', borderTop: '1px solid var(--gray-6)', display: 'flex', justifyContent: 'space-between', fontSize: 13 }}>
|
|
|
|
<div style={{ padding: '5px 10px', boxSizing: 'border-box', borderBottom: '1px solid var(--gray-6)', borderTop: '1px solid var(--gray-6)', display: 'flex', justifyContent: 'space-between', fontSize: '.8em' }}>
|
|
|
|
<div>{t('Segments total:')}</div>
|
|
|
|
<div>{t('Segments total:')}</div>
|
|
|
|
<div>{formatTimecode({ seconds: segmentsTotal })}</div>
|
|
|
|
<div>{formatTimecode({ seconds: segmentsTotal })}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
@ -586,6 +599,7 @@ function SegmentList({
|
|
|
|
onSelectAllMarkers={onSelectAllMarkers}
|
|
|
|
onSelectAllMarkers={onSelectAllMarkers}
|
|
|
|
onInvertSelectedSegments={onInvertSelectedSegments}
|
|
|
|
onInvertSelectedSegments={onInvertSelectedSegments}
|
|
|
|
onDuplicateSegmentClick={onDuplicateSegmentClick}
|
|
|
|
onDuplicateSegmentClick={onDuplicateSegmentClick}
|
|
|
|
|
|
|
|
getSegEstimatedSize={getSegEstimatedSize}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|