mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
UI improvements
- Upgrade evergreen-ui - improve stream selector - improve buttons - add default error toast - better error handling https://github.com/segmentio/evergreen/releases/tag/v5.0.0 https://evergreen.segment.com/introduction/v6-migration-guide
This commit is contained in:
parent
beb529fcbf
commit
e688f151b6
@ -53,7 +53,7 @@
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.23.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"evergreen-ui": "^4.23.0",
|
||||
"evergreen-ui": "^6.4.0",
|
||||
"fast-xml-parser": "^3.17.4",
|
||||
"file-url": "^3.0.0",
|
||||
"framer-motion": "1",
|
||||
|
581
src/App.jsx
581
src/App.jsx
@ -3,7 +3,7 @@ import { FaAngleLeft, FaWindowClose } from 'react-icons/fa';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import Swal from 'sweetalert2';
|
||||
import Lottie from 'react-lottie-player';
|
||||
import { SideSheet, Button, Position, SegmentedControl, Select } from 'evergreen-ui';
|
||||
import { SideSheet, Button, Position, ForkIcon, DisableIcon, Select, ThemeProvider } from 'evergreen-ui';
|
||||
import { useStateWithHistory } from 'react-use/lib/useStateWithHistory';
|
||||
import useDebounceOld from 'react-use/lib/useDebounce'; // Want to phase out this
|
||||
import { useDebounce } from 'use-debounce';
|
||||
@ -19,6 +19,7 @@ import sortBy from 'lodash/sortBy';
|
||||
import flatMap from 'lodash/flatMap';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import theme from './theme';
|
||||
import useTimelineScroll from './hooks/useTimelineScroll';
|
||||
import useUserPreferences from './hooks/useUserPreferences';
|
||||
import useFfmpegOperations from './hooks/useFfmpegOperations';
|
||||
@ -53,7 +54,7 @@ import {
|
||||
import { saveCsv, saveTsv, loadCsv, loadXmeml, loadCue, loadPbf, loadMplayerEdl, saveCsvHuman, saveLlcProject, loadLlcProject } from './edlStore';
|
||||
import { formatYouTube } from './edlFormats';
|
||||
import {
|
||||
getOutPath, toast, errorToast, showFfmpegFail, setFileNameTitle, getOutDir, withBlur,
|
||||
getOutPath, toast, errorToast, handleError, showFfmpegFail, setFileNameTitle, getOutDir, withBlur,
|
||||
checkDirWriteAccess, dirExists, openDirToast, isMasBuild, isStoreBuild, dragPreventer, doesPlayerSupportFile,
|
||||
isDurationValid, isWindows, filenamify, getOutFileExtension, generateSegFileName, defaultOutSegTemplate,
|
||||
hasDuplicates, havePermissionToReadFile, isMac, getFileBaseName, resolvePathIfNeeded, pathExists,
|
||||
@ -469,7 +470,7 @@ const App = memo(() => {
|
||||
} */
|
||||
setCutTime('start', startTime);
|
||||
} catch (err) {
|
||||
errorToast(err.message);
|
||||
handleError(err);
|
||||
}
|
||||
}
|
||||
}, [setCutTime, currentCutSeg, addCutSegment, filePath]);
|
||||
@ -486,7 +487,7 @@ const App = memo(() => {
|
||||
} */
|
||||
setCutTime('end', endTime);
|
||||
} catch (err) {
|
||||
errorToast(err.message);
|
||||
handleError(err);
|
||||
}
|
||||
}, [setCutTime, filePath]);
|
||||
|
||||
@ -1620,7 +1621,7 @@ const App = memo(() => {
|
||||
return;
|
||||
}
|
||||
if (openFileResponse === 'tracks') {
|
||||
addStreamSourceFile(firstFilePath);
|
||||
await addStreamSourceFile(firstFilePath);
|
||||
setStreamsSelectorShown(true);
|
||||
return;
|
||||
}
|
||||
@ -1929,9 +1930,13 @@ const App = memo(() => {
|
||||
]);
|
||||
|
||||
async function showAddStreamSourceDialog() {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] });
|
||||
if (canceled || filePaths.length < 1) return;
|
||||
await addStreamSourceFile(filePaths[0]);
|
||||
try {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] });
|
||||
if (canceled || filePaths.length < 1) return;
|
||||
await addStreamSourceFile(filePaths[0]);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -1961,9 +1966,9 @@ const App = memo(() => {
|
||||
));
|
||||
}
|
||||
|
||||
const renderOutFmt = useCallback((props) => (
|
||||
const renderOutFmt = useCallback((style) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<Select value={fileFormat || ''} title={i18n.t('Output format')} onChange={withBlur(e => onOutputFormatUserChange(e.target.value))} {...props}>
|
||||
<Select style={style} value={fileFormat || ''} title={i18n.t('Output format')} onChange={withBlur(e => onOutputFormatUserChange(e.target.value))}>
|
||||
<option key="disabled1" value="" disabled>{i18n.t('Format')}</option>
|
||||
|
||||
{detectedFileFormat && (
|
||||
@ -1992,11 +1997,9 @@ const App = memo(() => {
|
||||
), [captureFormat, toggleCaptureFormat]);
|
||||
|
||||
const AutoExportToggler = useCallback(() => (
|
||||
<SegmentedControl
|
||||
options={[{ label: i18n.t('Extract'), value: 'extract' }, { label: i18n.t('Discard'), value: 'discard' }]}
|
||||
value={autoExportExtraStreams ? 'extract' : 'discard'}
|
||||
onChange={value => setAutoExportExtraStreams(value === 'extract')}
|
||||
/>
|
||||
<Button intent={autoExportExtraStreams === 'extract' ? 'success' : 'danger'} iconBefore={autoExportExtraStreams === 'extract' ? ForkIcon : DisableIcon} onClick={() => setAutoExportExtraStreams(autoExportExtraStreams === 'extract' ? 'discard' : 'extract')}>
|
||||
{autoExportExtraStreams === 'extract' ? i18n.t('Extract') : i18n.t('Discard')}
|
||||
</Button>
|
||||
), [autoExportExtraStreams, setAutoExportExtraStreams]);
|
||||
|
||||
const onTunerRequested = useCallback((type) => {
|
||||
@ -2109,304 +2112,306 @@ const App = memo(() => {
|
||||
// throw new Error('Test');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="no-user-select" style={{ background: controlsBackground, height: topBarHeight, display: 'flex', alignItems: 'center', padding: '0 5px', justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
||||
<SideSheet
|
||||
width={700}
|
||||
containerProps={{ style: { maxWidth: '100%' } }}
|
||||
position={Position.LEFT}
|
||||
isShown={streamsSelectorShown}
|
||||
onCloseComplete={() => setStreamsSelectorShown(false)}
|
||||
>
|
||||
<StreamsSelector
|
||||
mainFilePath={filePath}
|
||||
mainFileFormatData={fileFormatData}
|
||||
externalFiles={externalStreamFiles}
|
||||
setExternalFiles={setExternalStreamFiles}
|
||||
showAddStreamSourceDialog={showAddStreamSourceDialog}
|
||||
streams={mainStreams}
|
||||
isCopyingStreamId={isCopyingStreamId}
|
||||
toggleCopyStreamId={toggleCopyStreamId}
|
||||
setCopyStreamIdsForPath={setCopyStreamIdsForPath}
|
||||
onExtractAllStreamsPress={extractAllStreams}
|
||||
onExtractStreamPress={extractSingleStream}
|
||||
areWeCutting={areWeCutting}
|
||||
shortestFlag={shortestFlag}
|
||||
setShortestFlag={setShortestFlag}
|
||||
nonCopiedExtraStreams={nonCopiedExtraStreams}
|
||||
AutoExportToggler={AutoExportToggler}
|
||||
customTagsByFile={customTagsByFile}
|
||||
setCustomTagsByFile={setCustomTagsByFile}
|
||||
customTagsByStreamId={customTagsByStreamId}
|
||||
setCustomTagsByStreamId={setCustomTagsByStreamId}
|
||||
<ThemeProvider value={theme}>
|
||||
<div>
|
||||
<div className="no-user-select" style={{ background: controlsBackground, height: topBarHeight, display: 'flex', alignItems: 'center', padding: '0 5px', justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
||||
<SideSheet
|
||||
width={700}
|
||||
containerProps={{ style: { maxWidth: '100%' } }}
|
||||
position={Position.LEFT}
|
||||
isShown={streamsSelectorShown}
|
||||
onCloseComplete={() => setStreamsSelectorShown(false)}
|
||||
>
|
||||
<StreamsSelector
|
||||
mainFilePath={filePath}
|
||||
mainFileFormatData={fileFormatData}
|
||||
externalFiles={externalStreamFiles}
|
||||
setExternalFiles={setExternalStreamFiles}
|
||||
showAddStreamSourceDialog={showAddStreamSourceDialog}
|
||||
streams={mainStreams}
|
||||
isCopyingStreamId={isCopyingStreamId}
|
||||
toggleCopyStreamId={toggleCopyStreamId}
|
||||
setCopyStreamIdsForPath={setCopyStreamIdsForPath}
|
||||
onExtractAllStreamsPress={extractAllStreams}
|
||||
onExtractStreamPress={extractSingleStream}
|
||||
areWeCutting={areWeCutting}
|
||||
shortestFlag={shortestFlag}
|
||||
setShortestFlag={setShortestFlag}
|
||||
nonCopiedExtraStreams={nonCopiedExtraStreams}
|
||||
AutoExportToggler={AutoExportToggler}
|
||||
customTagsByFile={customTagsByFile}
|
||||
setCustomTagsByFile={setCustomTagsByFile}
|
||||
customTagsByStreamId={customTagsByStreamId}
|
||||
setCustomTagsByStreamId={setCustomTagsByStreamId}
|
||||
/>
|
||||
</SideSheet>
|
||||
|
||||
<TopMenu
|
||||
filePath={filePath}
|
||||
height={topBarHeight}
|
||||
copyAnyAudioTrack={copyAnyAudioTrack}
|
||||
toggleStripAudio={toggleStripAudio}
|
||||
customOutDir={customOutDir}
|
||||
changeOutDir={changeOutDir}
|
||||
clearOutDir={clearOutDir}
|
||||
isCustomFormatSelected={isCustomFormatSelected}
|
||||
renderOutFmt={renderOutFmt}
|
||||
toggleHelp={toggleHelp}
|
||||
toggleSettings={toggleSettings}
|
||||
numStreamsToCopy={numStreamsToCopy}
|
||||
numStreamsTotal={numStreamsTotal}
|
||||
setStreamsSelectorShown={setStreamsSelectorShown}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
autoMerge={autoMerge}
|
||||
setAutoMerge={setAutoMerge}
|
||||
autoDeleteMergedSegments={autoDeleteMergedSegments}
|
||||
setAutoDeleteMergedSegments={setAutoDeleteMergedSegments}
|
||||
outFormatLocked={outFormatLocked}
|
||||
onOutFormatLockedClick={onOutFormatLockedClick}
|
||||
simpleMode={simpleMode}
|
||||
/>
|
||||
</SideSheet>
|
||||
</div>
|
||||
|
||||
<TopMenu
|
||||
filePath={filePath}
|
||||
height={topBarHeight}
|
||||
copyAnyAudioTrack={copyAnyAudioTrack}
|
||||
toggleStripAudio={toggleStripAudio}
|
||||
customOutDir={customOutDir}
|
||||
changeOutDir={changeOutDir}
|
||||
clearOutDir={clearOutDir}
|
||||
isCustomFormatSelected={isCustomFormatSelected}
|
||||
renderOutFmt={renderOutFmt}
|
||||
toggleHelp={toggleHelp}
|
||||
toggleSettings={toggleSettings}
|
||||
numStreamsToCopy={numStreamsToCopy}
|
||||
numStreamsTotal={numStreamsTotal}
|
||||
setStreamsSelectorShown={setStreamsSelectorShown}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
autoMerge={autoMerge}
|
||||
setAutoMerge={setAutoMerge}
|
||||
autoDeleteMergedSegments={autoDeleteMergedSegments}
|
||||
setAutoDeleteMergedSegments={setAutoDeleteMergedSegments}
|
||||
outFormatLocked={outFormatLocked}
|
||||
onOutFormatLockedClick={onOutFormatLockedClick}
|
||||
simpleMode={simpleMode}
|
||||
/>
|
||||
</div>
|
||||
{!isFileOpened && <NoFileLoaded topBarHeight={topBarHeight} bottomBarHeight={bottomBarHeight} mifiLink={mifiLink} toggleHelp={toggleHelp} currentCutSeg={currentCutSeg} simpleMode={simpleMode} toggleSimpleMode={toggleSimpleMode} />}
|
||||
|
||||
{!isFileOpened && <NoFileLoaded topBarHeight={topBarHeight} bottomBarHeight={bottomBarHeight} mifiLink={mifiLink} toggleHelp={toggleHelp} currentCutSeg={currentCutSeg} simpleMode={simpleMode} toggleSimpleMode={toggleSimpleMode} />}
|
||||
<AnimatePresence>
|
||||
{working && (
|
||||
<div style={{
|
||||
position: 'absolute', zIndex: 1, bottom: bottomBarHeight, top: topBarHeight, left: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
style={{ background: primaryColor, boxShadow: `${primaryColor} 0px 0px 20px 25px`, borderRadius: 20, paddingBottom: 15, color: 'white', textAlign: 'center', fontSize: 14 }}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0 }}
|
||||
>
|
||||
<div style={{ width: 150, height: 150 }}>
|
||||
<Lottie
|
||||
loop
|
||||
animationData={loadingLottie}
|
||||
play
|
||||
style={{ width: '170%', height: '130%', marginLeft: '-35%', marginTop: '-29%', pointerEvents: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{working && (
|
||||
<div style={{ marginTop: 10, width: 150 }}>
|
||||
{working}...
|
||||
</div>
|
||||
|
||||
{(cutProgress != null) && (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
{`${(cutProgress * 100).toFixed(1)} %`}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div className="no-user-select" style={{ position: 'absolute', top: topBarHeight, left: 0, right: sideBarWidth, bottom: bottomBarHeight, visibility: !isFileOpened ? 'hidden' : undefined }} onWheel={onTimelineWheel}>
|
||||
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
||||
<video
|
||||
muted={playbackVolume === 0}
|
||||
ref={videoRef}
|
||||
style={videoStyle}
|
||||
src={fileUri}
|
||||
onPlay={onSartPlaying}
|
||||
onPause={onStopPlaying}
|
||||
onDurationChange={onDurationChange}
|
||||
onTimeUpdate={onTimeUpdate}
|
||||
onError={onVideoError}
|
||||
/>
|
||||
|
||||
{canvasPlayerEnabled && <Canvas rotate={effectiveRotation} filePath={filePath} width={mainVideoStream.width} height={mainVideoStream.height} streamIndex={mainVideoStream.index} playerTime={playerTime} commandedTime={commandedTime} playing={playing} />}
|
||||
</div>
|
||||
|
||||
{isRotationSet && !hideCanvasPreview && (
|
||||
<div style={{
|
||||
position: 'absolute', zIndex: 1, bottom: bottomBarHeight, top: topBarHeight, left: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
position: 'absolute', top: topBarHeight, marginTop: '1em', marginRight: '1em', right: sideBarWidth, color: 'white',
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
style={{ background: primaryColor, boxShadow: `${primaryColor} 0px 0px 20px 25px`, borderRadius: 20, paddingBottom: 15, color: 'white', textAlign: 'center', fontSize: 14 }}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0 }}
|
||||
>
|
||||
<div style={{ width: 150, height: 150 }}>
|
||||
<Lottie
|
||||
loop
|
||||
animationData={loadingLottie}
|
||||
play
|
||||
style={{ width: '170%', height: '130%', marginLeft: '-35%', marginTop: '-29%', pointerEvents: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 10, width: 150 }}>
|
||||
{working}...
|
||||
</div>
|
||||
|
||||
{(cutProgress != null) && (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
{`${(cutProgress * 100).toFixed(1)} %`}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
{t('Rotation preview')}
|
||||
{!canvasPlayerRequired && <FaWindowClose role="button" style={{ cursor: 'pointer', verticalAlign: 'middle', padding: 10 }} onClick={() => setHideCanvasPreview(true)} />}
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div className="no-user-select" style={{ position: 'absolute', top: topBarHeight, left: 0, right: sideBarWidth, bottom: bottomBarHeight, visibility: !isFileOpened ? 'hidden' : undefined }} onWheel={onTimelineWheel}>
|
||||
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
||||
<video
|
||||
muted={playbackVolume === 0}
|
||||
ref={videoRef}
|
||||
style={videoStyle}
|
||||
src={fileUri}
|
||||
onPlay={onSartPlaying}
|
||||
onPause={onStopPlaying}
|
||||
onDurationChange={onDurationChange}
|
||||
onTimeUpdate={onTimeUpdate}
|
||||
onError={onVideoError}
|
||||
/>
|
||||
{isFileOpened && (
|
||||
<>
|
||||
<div
|
||||
className="no-user-select"
|
||||
style={{
|
||||
position: 'absolute', right: sideBarWidth, bottom: bottomBarHeight, marginBottom: 10, color: 'rgba(255,255,255,0.7)', display: 'flex', alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<VolumeControl playbackVolume={playbackVolume} setPlaybackVolume={setPlaybackVolume} usingDummyVideo={usingDummyVideo} />
|
||||
|
||||
{canvasPlayerEnabled && <Canvas rotate={effectiveRotation} filePath={filePath} width={mainVideoStream.width} height={mainVideoStream.height} streamIndex={mainVideoStream.index} playerTime={playerTime} commandedTime={commandedTime} playing={playing} />}
|
||||
</div>
|
||||
|
||||
{isRotationSet && !hideCanvasPreview && (
|
||||
<div style={{
|
||||
position: 'absolute', top: topBarHeight, marginTop: '1em', marginRight: '1em', right: sideBarWidth, color: 'white',
|
||||
}}
|
||||
>
|
||||
{t('Rotation preview')}
|
||||
{!canvasPlayerRequired && <FaWindowClose role="button" style={{ cursor: 'pointer', verticalAlign: 'middle', padding: 10 }} onClick={() => setHideCanvasPreview(true)} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFileOpened && (
|
||||
<>
|
||||
<div
|
||||
className="no-user-select"
|
||||
style={{
|
||||
position: 'absolute', right: sideBarWidth, bottom: bottomBarHeight, marginBottom: 10, color: 'rgba(255,255,255,0.7)', display: 'flex', alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<VolumeControl playbackVolume={playbackVolume} setPlaybackVolume={setPlaybackVolume} usingDummyVideo={usingDummyVideo} />
|
||||
|
||||
{!showSideBar && (
|
||||
<FaAngleLeft
|
||||
title={t('Show sidebar')}
|
||||
size={30}
|
||||
role="button"
|
||||
style={{ margin: '0 7px' }}
|
||||
onClick={toggleSideBar}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{showSideBar && (
|
||||
<motion.div
|
||||
style={{ position: 'absolute', width: sideBarWidth, right: 0, bottom: bottomBarHeight, top: topBarHeight, background: controlsBackground, color: 'rgba(255,255,255,0.7)', display: 'flex', flexDirection: 'column' }}
|
||||
initial={{ x: sideBarWidth }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: sideBarWidth }}
|
||||
>
|
||||
<SegmentList
|
||||
simpleMode={simpleMode}
|
||||
currentSegIndex={currentSegIndexSafe}
|
||||
outSegments={outSegments}
|
||||
cutSegments={apparentCutSegments}
|
||||
getFrameCount={getFrameCount}
|
||||
formatTimecode={formatTimecode}
|
||||
invertCutSegments={invertCutSegments}
|
||||
onSegClick={setCurrentSegIndex}
|
||||
updateSegOrder={updateSegOrder}
|
||||
updateSegOrders={updateSegOrders}
|
||||
onLabelSegmentPress={onLabelSegmentPress}
|
||||
currentCutSeg={currentCutSeg}
|
||||
segmentAtCursor={segmentAtCursor}
|
||||
addCutSegment={addCutSegment}
|
||||
removeCutSegment={removeCutSegment}
|
||||
toggleSideBar={toggleSideBar}
|
||||
splitCurrentSegment={splitCurrentSegment}
|
||||
enabledOutSegmentsRaw={enabledOutSegmentsRaw}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
onExportSingleSegmentClick={onExportSingleSegmentClick}
|
||||
onExportSegmentEnabledToggle={onExportSegmentEnabledToggle}
|
||||
onExportSegmentDisableAll={onExportSegmentDisableAll}
|
||||
onExportSegmentEnableAll={onExportSegmentEnableAll}
|
||||
jumpSegStart={jumpSegStart}
|
||||
jumpSegEnd={jumpSegEnd}
|
||||
onViewSegmentTagsPress={onViewSegmentTagsPress}
|
||||
{!showSideBar && (
|
||||
<FaAngleLeft
|
||||
title={t('Show sidebar')}
|
||||
size={30}
|
||||
role="button"
|
||||
style={{ margin: '0 7px' }}
|
||||
onClick={toggleSideBar}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="no-user-select"
|
||||
style={{ background: controlsBackground, position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}
|
||||
animate={{ height: bottomBarHeight }}
|
||||
>
|
||||
<Timeline
|
||||
shouldShowKeyframes={shouldShowKeyframes}
|
||||
waveform={waveform}
|
||||
shouldShowWaveform={shouldShowWaveform}
|
||||
waveformEnabled={waveformEnabled}
|
||||
thumbnailsEnabled={thumbnailsEnabled}
|
||||
neighbouringFrames={neighbouringFrames}
|
||||
thumbnails={thumbnailsSorted}
|
||||
getCurrentTime={getCurrentTime}
|
||||
commandedTimeRef={commandedTimeRef}
|
||||
startTimeOffset={startTimeOffset}
|
||||
playerTime={playerTime}
|
||||
commandedTime={commandedTime}
|
||||
zoom={zoom}
|
||||
seekAbs={seekAbs}
|
||||
durationSafe={durationSafe}
|
||||
apparentCutSegments={apparentCutSegments}
|
||||
setCurrentSegIndex={setCurrentSegIndex}
|
||||
currentSegIndexSafe={currentSegIndexSafe}
|
||||
invertCutSegments={invertCutSegments}
|
||||
inverseCutSegments={inverseCutSegments}
|
||||
formatTimecode={formatTimecode}
|
||||
timelineHeight={timelineHeight}
|
||||
onZoomWindowStartTimeChange={setZoomWindowStartTime}
|
||||
playing={playing}
|
||||
isFileOpened={isFileOpened}
|
||||
onWheel={onTimelineWheel}
|
||||
/>
|
||||
<AnimatePresence>
|
||||
{showSideBar && (
|
||||
<motion.div
|
||||
style={{ position: 'absolute', width: sideBarWidth, right: 0, bottom: bottomBarHeight, top: topBarHeight, background: controlsBackground, color: 'rgba(255,255,255,0.7)', display: 'flex', flexDirection: 'column' }}
|
||||
initial={{ x: sideBarWidth }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: sideBarWidth }}
|
||||
>
|
||||
<SegmentList
|
||||
simpleMode={simpleMode}
|
||||
currentSegIndex={currentSegIndexSafe}
|
||||
outSegments={outSegments}
|
||||
cutSegments={apparentCutSegments}
|
||||
getFrameCount={getFrameCount}
|
||||
formatTimecode={formatTimecode}
|
||||
invertCutSegments={invertCutSegments}
|
||||
onSegClick={setCurrentSegIndex}
|
||||
updateSegOrder={updateSegOrder}
|
||||
updateSegOrders={updateSegOrders}
|
||||
onLabelSegmentPress={onLabelSegmentPress}
|
||||
currentCutSeg={currentCutSeg}
|
||||
segmentAtCursor={segmentAtCursor}
|
||||
addCutSegment={addCutSegment}
|
||||
removeCutSegment={removeCutSegment}
|
||||
toggleSideBar={toggleSideBar}
|
||||
splitCurrentSegment={splitCurrentSegment}
|
||||
enabledOutSegmentsRaw={enabledOutSegmentsRaw}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
onExportSingleSegmentClick={onExportSingleSegmentClick}
|
||||
onExportSegmentEnabledToggle={onExportSegmentEnabledToggle}
|
||||
onExportSegmentDisableAll={onExportSegmentDisableAll}
|
||||
onExportSegmentEnableAll={onExportSegmentEnableAll}
|
||||
jumpSegStart={jumpSegStart}
|
||||
jumpSegEnd={jumpSegEnd}
|
||||
onViewSegmentTagsPress={onViewSegmentTagsPress}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
|
||||
<TimelineControls
|
||||
seekAbs={seekAbs}
|
||||
currentSegIndexSafe={currentSegIndexSafe}
|
||||
cutSegments={cutSegments}
|
||||
currentCutSeg={currentCutSeg}
|
||||
setCutStart={setCutStart}
|
||||
setCutEnd={setCutEnd}
|
||||
setCurrentSegIndex={setCurrentSegIndex}
|
||||
cutStartTimeManual={cutStartTimeManual}
|
||||
setCutStartTimeManual={setCutStartTimeManual}
|
||||
cutEndTimeManual={cutEndTimeManual}
|
||||
setCutEndTimeManual={setCutEndTimeManual}
|
||||
duration={durationSafe}
|
||||
jumpCutEnd={jumpCutEnd}
|
||||
jumpCutStart={jumpCutStart}
|
||||
startTimeOffset={startTimeOffset}
|
||||
setCutTime={setCutTime}
|
||||
currentApparentCutSeg={currentApparentCutSeg}
|
||||
playing={playing}
|
||||
shortStep={shortStep}
|
||||
seekClosestKeyframe={seekClosestKeyframe}
|
||||
togglePlay={togglePlay}
|
||||
setTimelineMode={setTimelineMode}
|
||||
timelineMode={timelineMode}
|
||||
hasAudio={hasAudio}
|
||||
hasVideo={hasVideo}
|
||||
keyframesEnabled={keyframesEnabled}
|
||||
toggleKeyframesEnabled={toggleKeyframesEnabled}
|
||||
simpleMode={simpleMode}
|
||||
/>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', height: 36 }}>
|
||||
<LeftMenu
|
||||
<motion.div
|
||||
className="no-user-select"
|
||||
style={{ background: controlsBackground, position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}
|
||||
animate={{ height: bottomBarHeight }}
|
||||
>
|
||||
<Timeline
|
||||
shouldShowKeyframes={shouldShowKeyframes}
|
||||
waveform={waveform}
|
||||
shouldShowWaveform={shouldShowWaveform}
|
||||
waveformEnabled={waveformEnabled}
|
||||
thumbnailsEnabled={thumbnailsEnabled}
|
||||
neighbouringFrames={neighbouringFrames}
|
||||
thumbnails={thumbnailsSorted}
|
||||
getCurrentTime={getCurrentTime}
|
||||
commandedTimeRef={commandedTimeRef}
|
||||
startTimeOffset={startTimeOffset}
|
||||
playerTime={playerTime}
|
||||
commandedTime={commandedTime}
|
||||
zoom={zoom}
|
||||
setZoom={setZoom}
|
||||
seekAbs={seekAbs}
|
||||
durationSafe={durationSafe}
|
||||
apparentCutSegments={apparentCutSegments}
|
||||
setCurrentSegIndex={setCurrentSegIndex}
|
||||
currentSegIndexSafe={currentSegIndexSafe}
|
||||
invertCutSegments={invertCutSegments}
|
||||
setInvertCutSegments={setInvertCutSegments}
|
||||
toggleComfortZoom={toggleComfortZoom}
|
||||
simpleMode={simpleMode}
|
||||
toggleSimpleMode={toggleSimpleMode}
|
||||
inverseCutSegments={inverseCutSegments}
|
||||
formatTimecode={formatTimecode}
|
||||
timelineHeight={timelineHeight}
|
||||
onZoomWindowStartTimeChange={setZoomWindowStartTime}
|
||||
playing={playing}
|
||||
isFileOpened={isFileOpened}
|
||||
onWheel={onTimelineWheel}
|
||||
/>
|
||||
|
||||
<RightMenu
|
||||
<TimelineControls
|
||||
seekAbs={seekAbs}
|
||||
currentSegIndexSafe={currentSegIndexSafe}
|
||||
cutSegments={cutSegments}
|
||||
currentCutSeg={currentCutSeg}
|
||||
setCutStart={setCutStart}
|
||||
setCutEnd={setCutEnd}
|
||||
setCurrentSegIndex={setCurrentSegIndex}
|
||||
cutStartTimeManual={cutStartTimeManual}
|
||||
setCutStartTimeManual={setCutStartTimeManual}
|
||||
cutEndTimeManual={cutEndTimeManual}
|
||||
setCutEndTimeManual={setCutEndTimeManual}
|
||||
duration={durationSafe}
|
||||
jumpCutEnd={jumpCutEnd}
|
||||
jumpCutStart={jumpCutStart}
|
||||
startTimeOffset={startTimeOffset}
|
||||
setCutTime={setCutTime}
|
||||
currentApparentCutSeg={currentApparentCutSeg}
|
||||
playing={playing}
|
||||
shortStep={shortStep}
|
||||
seekClosestKeyframe={seekClosestKeyframe}
|
||||
togglePlay={togglePlay}
|
||||
setTimelineMode={setTimelineMode}
|
||||
timelineMode={timelineMode}
|
||||
hasAudio={hasAudio}
|
||||
hasVideo={hasVideo}
|
||||
isRotationSet={isRotationSet}
|
||||
rotation={rotation}
|
||||
areWeCutting={areWeCutting}
|
||||
autoMerge={autoMerge}
|
||||
increaseRotation={increaseRotation}
|
||||
cleanupFiles={cleanupFiles}
|
||||
renderCaptureFormatButton={renderCaptureFormatButton}
|
||||
capture={capture}
|
||||
onExportPress={onExportPress}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
exportConfirmEnabled={exportConfirmEnabled}
|
||||
toggleExportConfirmEnabled={toggleExportConfirmEnabled}
|
||||
keyframesEnabled={keyframesEnabled}
|
||||
toggleKeyframesEnabled={toggleKeyframesEnabled}
|
||||
simpleMode={simpleMode}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<ExportConfirm filePath={filePath} autoMerge={autoMerge} setAutoMerge={setAutoMerge} areWeCutting={areWeCutting} enabledOutSegments={enabledOutSegments} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} keyframeCut={keyframeCut} toggleKeyframeCut={toggleKeyframeCut} renderOutFmt={renderOutFmt} preserveMovData={preserveMovData} togglePreserveMovData={togglePreserveMovData} movFastStart={movFastStart} toggleMovFastStart={toggleMovFastStart} avoidNegativeTs={avoidNegativeTs} setAvoidNegativeTs={setAvoidNegativeTs} changeOutDir={changeOutDir} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} setStreamsSelectorShown={setStreamsSelectorShown} exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} segmentsToChapters={segmentsToChapters} toggleSegmentsToChapters={toggleSegmentsToChapters} outFormat={fileFormat} preserveMetadataOnMerge={preserveMetadataOnMerge} togglePreserveMetadataOnMerge={togglePreserveMetadataOnMerge} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} generateOutSegFileNames={generateOutSegFileNames} currentSegIndexSafe={currentSegIndexSafe} isOutSegFileNamesValid={isOutSegFileNamesValid} autoDeleteMergedSegments={autoDeleteMergedSegments} setAutoDeleteMergedSegments={setAutoDeleteMergedSegments} safeOutputFileName={safeOutputFileName} toggleSafeOutputFileName={toggleSafeOutputFileName} />
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', height: 36 }}>
|
||||
<LeftMenu
|
||||
zoom={zoom}
|
||||
setZoom={setZoom}
|
||||
invertCutSegments={invertCutSegments}
|
||||
setInvertCutSegments={setInvertCutSegments}
|
||||
toggleComfortZoom={toggleComfortZoom}
|
||||
simpleMode={simpleMode}
|
||||
toggleSimpleMode={toggleSimpleMode}
|
||||
/>
|
||||
|
||||
<HelpSheet
|
||||
visible={helpVisible}
|
||||
onTogglePress={toggleHelp}
|
||||
ffmpegCommandLog={ffmpegCommandLog}
|
||||
currentCutSeg={currentCutSeg}
|
||||
/>
|
||||
<RightMenu
|
||||
hasVideo={hasVideo}
|
||||
isRotationSet={isRotationSet}
|
||||
rotation={rotation}
|
||||
areWeCutting={areWeCutting}
|
||||
autoMerge={autoMerge}
|
||||
increaseRotation={increaseRotation}
|
||||
cleanupFiles={cleanupFiles}
|
||||
renderCaptureFormatButton={renderCaptureFormatButton}
|
||||
capture={capture}
|
||||
onExportPress={onExportPress}
|
||||
enabledOutSegments={enabledOutSegments}
|
||||
exportConfirmEnabled={exportConfirmEnabled}
|
||||
toggleExportConfirmEnabled={toggleExportConfirmEnabled}
|
||||
simpleMode={simpleMode}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<SettingsSheet
|
||||
visible={settingsVisible}
|
||||
onTogglePress={toggleSettings}
|
||||
renderSettings={renderSettings}
|
||||
/>
|
||||
<ExportConfirm filePath={filePath} autoMerge={autoMerge} setAutoMerge={setAutoMerge} areWeCutting={areWeCutting} enabledOutSegments={enabledOutSegments} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} keyframeCut={keyframeCut} toggleKeyframeCut={toggleKeyframeCut} renderOutFmt={renderOutFmt} preserveMovData={preserveMovData} togglePreserveMovData={togglePreserveMovData} movFastStart={movFastStart} toggleMovFastStart={toggleMovFastStart} avoidNegativeTs={avoidNegativeTs} setAvoidNegativeTs={setAvoidNegativeTs} changeOutDir={changeOutDir} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} setStreamsSelectorShown={setStreamsSelectorShown} exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} segmentsToChapters={segmentsToChapters} toggleSegmentsToChapters={toggleSegmentsToChapters} outFormat={fileFormat} preserveMetadataOnMerge={preserveMetadataOnMerge} togglePreserveMetadataOnMerge={togglePreserveMetadataOnMerge} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} generateOutSegFileNames={generateOutSegFileNames} currentSegIndexSafe={currentSegIndexSafe} isOutSegFileNamesValid={isOutSegFileNamesValid} autoDeleteMergedSegments={autoDeleteMergedSegments} setAutoDeleteMergedSegments={setAutoDeleteMergedSegments} safeOutputFileName={safeOutputFileName} toggleSafeOutputFileName={toggleSafeOutputFileName} />
|
||||
|
||||
{tunerVisible && renderTuner(tunerVisible)}
|
||||
</div>
|
||||
<HelpSheet
|
||||
visible={helpVisible}
|
||||
onTogglePress={toggleHelp}
|
||||
ffmpegCommandLog={ffmpegCommandLog}
|
||||
currentCutSeg={currentCutSeg}
|
||||
/>
|
||||
|
||||
<SettingsSheet
|
||||
visible={settingsVisible}
|
||||
onTogglePress={toggleSettings}
|
||||
renderSettings={renderSettings}
|
||||
/>
|
||||
|
||||
{tunerVisible && renderTuner(tunerVisible)}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { memo } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Button, Select } from 'evergreen-ui';
|
||||
import { Button, Select, CrossIcon } from 'evergreen-ui';
|
||||
import i18n from 'i18next';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { IoIosHelpCircle } from 'react-icons/io';
|
||||
@ -30,7 +30,7 @@ const sheetStyle = {
|
||||
display: 'flex',
|
||||
};
|
||||
|
||||
const boxStyle = { margin: '15px 15px 50px 15px', background: 'rgba(25, 25, 25, 0.6)', borderRadius: 10, padding: '10px 20px', minHeight: 450 };
|
||||
const boxStyle = { margin: '15px 15px 50px 15px', background: 'rgba(25, 25, 25, 0.6)', borderRadius: 10, padding: '10px 20px', minHeight: 450, position: 'relative' };
|
||||
|
||||
const outDirStyle = { background: 'rgb(193, 98, 0)', borderRadius: '.4em', padding: '0 .3em', wordBreak: 'break-all', cursor: 'pointer' };
|
||||
|
||||
@ -111,6 +111,8 @@ const ExportConfirm = memo(({
|
||||
>
|
||||
<div style={{ margin: 'auto' }}>
|
||||
<div style={boxStyle}>
|
||||
<CrossIcon size={24} style={{ position: 'absolute', right: 0, top: 0, padding: 15, boxSizing: 'content-box', cursor: 'pointer' }} role="button" onClick={onClosePress} />
|
||||
|
||||
<h2 style={{ marginTop: 0 }}>{t('Export options')}</h2>
|
||||
<ul>
|
||||
{enabledOutSegments.length >= 2 && <li>{t('Merge {{segments}} cut segments to one file?', { segments: enabledOutSegments.length })} <MergeExportButton autoMerge={autoMerge} enabledOutSegments={enabledOutSegments} setAutoMerge={setAutoMerge} autoDeleteMergedSegments={autoDeleteMergedSegments} setAutoDeleteMergedSegments={setAutoDeleteMergedSegments} /></li>}
|
||||
@ -190,11 +192,7 @@ const ExportConfirm = memo(({
|
||||
transition={{ duration: 0.4, easings: ['easeOut'] }}
|
||||
style={{ display: 'flex', alignItems: 'flex-end' }}
|
||||
>
|
||||
<Button appearance="minimal" iconBefore="arrow-left" height={24} onClick={onClosePress} style={{ marginRight: 10, color: 'white' }}>
|
||||
{i18n.t('Back')}
|
||||
</Button>
|
||||
|
||||
<ToggleExportConfirm exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} />
|
||||
<ToggleExportConfirm size={25} exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} />
|
||||
<div style={{ fontSize: 13, marginLeft: 3, marginRight: 7, maxWidth: 120, lineHeight: '100%', color: exportConfirmEnabled ? 'white' : 'rgba(255,255,255,0.3)', cursor: 'pointer' }} role="button" onClick={toggleExportConfirmEnabled}>{t('Show this page before exporting?')}</div>
|
||||
</motion.div>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Button, Table, SegmentedControl, Checkbox, Select } from 'evergreen-ui';
|
||||
import { FaYinYang } from 'react-icons/fa';
|
||||
import { Button, Table, NumericalIcon, KeyIcon, FolderCloseIcon, DocumentIcon, TimeIcon, Checkbox, Select } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
||||
@ -66,11 +67,11 @@ const Settings = memo(({
|
||||
<Row>
|
||||
<KeyCell>
|
||||
{t('Working directory')}<br />
|
||||
{t('This is where working files, exported files, project files (CSV) are stored.')}
|
||||
{t('This is where working files, exported files, project files (LLC) are stored.')}
|
||||
</KeyCell>
|
||||
<Table.TextCell>
|
||||
<Button onClick={changeOutDir}>
|
||||
{customOutDir ? t('Custom working directory') : t('Same directory as input file')}
|
||||
<Button iconBefore={customOutDir ? FolderCloseIcon : DocumentIcon} onClick={changeOutDir}>
|
||||
{customOutDir ? t('Custom working directory') : t('Same directory as input file')}...
|
||||
</Button>
|
||||
<div>{customOutDir}</div>
|
||||
</Table.TextCell>
|
||||
@ -79,11 +80,9 @@ const Settings = memo(({
|
||||
<Row>
|
||||
<KeyCell>{t('Set file modification date/time of output files to:')}</KeyCell>
|
||||
<Table.TextCell>
|
||||
<SegmentedControl
|
||||
options={[{ label: t('Source file\'s time'), value: 'true' }, { label: t('Current time'), value: 'false' }]}
|
||||
value={enableTransferTimestamps ? 'true' : 'false'}
|
||||
onChange={value => setEnableTransferTimestamps(value === 'true')}
|
||||
/>
|
||||
<Button iconBefore={enableTransferTimestamps ? DocumentIcon : TimeIcon} onClick={() => setEnableTransferTimestamps((v) => !v)}>
|
||||
{enableTransferTimestamps ? t('Source file\'s time') : t('Current time')}
|
||||
</Button>
|
||||
</Table.TextCell>
|
||||
</Row>
|
||||
|
||||
@ -94,26 +93,22 @@ const Settings = memo(({
|
||||
<b>{t('Normal cut')}</b>: {t('Accurate time but could leave an empty portion at the beginning of the video. Equiv to')} <i>ffmpeg -i -ss ...</i><br />
|
||||
</KeyCell>
|
||||
<Table.TextCell>
|
||||
<SegmentedControl
|
||||
options={[{ label: t('Keyframe cut'), value: 'keyframe' }, { label: t('Normal cut'), value: 'normal' }]}
|
||||
value={keyframeCut ? 'keyframe' : 'normal'}
|
||||
onChange={value => setKeyframeCut(value === 'keyframe')}
|
||||
/>
|
||||
<Button iconBefore={keyframeCut === 'keyframe' ? KeyIcon : undefined} onClick={() => setKeyframeCut(keyframeCut === 'keyframe' ? 'normal' : 'keyframe')}>
|
||||
{keyframeCut === 'keyframe' ? t('Keyframe cut') : t('Normal cut')}
|
||||
</Button>
|
||||
</Table.TextCell>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<KeyCell>
|
||||
<span role="img" aria-label="Yin Yang">☯️</span> {t('Choose cutting mode: Remove or keep selected segments from video when exporting?')}<br />
|
||||
{t('Choose cutting mode: Remove or keep selected segments from video when exporting?')}<br />
|
||||
<b>{t('Keep')}</b>: {t('The video inside segments will be kept, while the video outside will be discarded.')}<br />
|
||||
<b>{t('Remove')}</b>: {t('The video inside segments will be discarded, while the video surrounding them will be kept.')}
|
||||
</KeyCell>
|
||||
<Table.TextCell>
|
||||
<SegmentedControl
|
||||
options={[{ label: t('Remove'), value: 'discard' }, { label: t('Keep'), value: 'keep' }]}
|
||||
value={invertCutSegments ? 'discard' : 'keep'}
|
||||
onChange={value => setInvertCutSegments(value === 'discard')}
|
||||
/>
|
||||
<Button iconBefore={FaYinYang} appearance={invertCutSegments ? 'default' : 'primary'} intent="success" onClick={() => setInvertCutSegments((v) => !v)}>
|
||||
{invertCutSegments ? t('Remove') : t('Keep')}
|
||||
</Button>
|
||||
</Table.TextCell>
|
||||
</Row>
|
||||
|
||||
@ -164,11 +159,9 @@ const Settings = memo(({
|
||||
<Row>
|
||||
<KeyCell>{t('In timecode show')}</KeyCell>
|
||||
<Table.TextCell>
|
||||
<SegmentedControl
|
||||
options={[{ label: t('Frame numbers'), value: 'frames' }, { label: t('Millisecond fractions'), value: 'ms' }]}
|
||||
value={timecodeShowFrames ? 'frames' : 'ms'}
|
||||
onChange={value => setTimecodeShowFrames(value === 'frames')}
|
||||
/>
|
||||
<Button iconBefore={timecodeShowFrames ? NumericalIcon : TimeIcon} onClick={() => setTimecodeShowFrames((v) => !v)}>
|
||||
{timecodeShowFrames ? t('Frame numbers') : t('Millisecond fractions')}
|
||||
</Button>
|
||||
</Table.TextCell>
|
||||
</Row>
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { memo, useState, useMemo } from 'react';
|
||||
|
||||
import { FaVideo, FaVideoSlash, FaFileExport, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaTrashAlt, FaInfoCircle } from 'react-icons/fa';
|
||||
import { FaVideo, FaVideoSlash, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaTrashAlt, FaInfoCircle, FaFileExport } from 'react-icons/fa';
|
||||
import { GoFileBinary } from 'react-icons/go';
|
||||
import { FiEdit, FiCheck, FiTrash } from 'react-icons/fi';
|
||||
import { MdSubtitles } from 'react-icons/md';
|
||||
import { SegmentedControl, Dialog, Button } from 'evergreen-ui';
|
||||
import { SortAscIcon, SortDescIcon, Dialog, Button, PlusIcon, Pane, ForkIcon } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { askForMetadataKey, showJson5Dialog } from './dialogs';
|
||||
@ -89,7 +89,7 @@ const TagEditor = memo(({ existingTags, customTags, onTagChange, onTagReset }) =
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Button style={{ marginTop: 10 }} iconBefore="plus" onClick={onAddPress}>Add metadata</Button>
|
||||
<Button style={{ marginTop: 10 }} iconBefore={PlusIcon} onClick={onAddPress}>Add metadata</Button>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@ -207,7 +207,7 @@ const FileHeading = ({ path, formatData, onTrashClick, onEditClick }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', marginBottom: 10, alignItems: 'center' }}>
|
||||
<div style={{ display: 'flex', marginBottom: 15, marginLeft: 5, marginRight: 5, marginTop: 5, alignItems: 'center' }}>
|
||||
<div title={path} style={{ wordBreak: 'break-all', fontWeight: 'bold' }}>{path.replace(/.*\/([^/]+)$/, '$1')}</div>
|
||||
<FaInfoCircle role="button" onClick={() => onInfoClick(formatData, t('File info'))} size={20} style={{ padding: '0 5px 0 10px' }} />
|
||||
{onEditClick && <FiEdit title={t('Edit file metadata')} role="button" size={20} style={{ padding: '0 5px' }} onClick={onEditClick} />}
|
||||
@ -237,7 +237,7 @@ const Thead = () => {
|
||||
};
|
||||
|
||||
const tableStyle = { fontSize: 14, width: '100%' };
|
||||
const fileStyle = { marginBottom: 10, backgroundColor: 'rgba(0,0,0,0.04)', padding: 5, borderRadius: 7 };
|
||||
const fileStyle = { marginBottom: 20, padding: 5 };
|
||||
|
||||
const StreamsSelector = memo(({
|
||||
mainFilePath, mainFileFormatData, streams: mainFileStreams, isCopyingStreamId, toggleCopyStreamId,
|
||||
@ -273,7 +273,7 @@ const StreamsSelector = memo(({
|
||||
<div style={{ color: 'black', padding: 10 }}>
|
||||
<p>{t('Click to select which tracks to keep when exporting:')}</p>
|
||||
|
||||
<div style={fileStyle}>
|
||||
<Pane elevation={1} style={fileStyle}>
|
||||
{/* We only support editing main file metadata for now */}
|
||||
<FileHeading path={mainFilePath} formatData={mainFileFormatData} onEditClick={() => setEditingFile(mainFilePath)} />
|
||||
<table style={tableStyle}>
|
||||
@ -293,10 +293,10 @@ const StreamsSelector = memo(({
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Pane>
|
||||
|
||||
{externalFilesEntries.map(([path, { streams, formatData }]) => (
|
||||
<div key={path} style={fileStyle}>
|
||||
<Pane elevation={1} key={path} style={fileStyle}>
|
||||
<FileHeading path={path} formatData={formatData} onTrashClick={() => removeFile(path)} />
|
||||
<table style={tableStyle}>
|
||||
<Thead />
|
||||
@ -314,36 +314,32 @@ const StreamsSelector = memo(({
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Pane>
|
||||
))}
|
||||
|
||||
{externalFilesEntries.length > 0 && (
|
||||
<div style={{ margin: '10px 0' }}>
|
||||
<div>
|
||||
{t('When tracks have different lengths, do you want to make the output file as long as the longest or the shortest track?')}
|
||||
</div>
|
||||
<SegmentedControl
|
||||
options={[{ label: t('Longest'), value: 'longest' }, { label: t('Shortest'), value: 'shortest' }]}
|
||||
value={shortestFlag ? 'shortest' : 'longest'}
|
||||
onChange={value => setShortestFlag(value === 'shortest')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Button iconBefore={() => <FaFileImport size={16} />} onClick={showAddStreamSourceDialog}>
|
||||
{t('Include more tracks from other file')}
|
||||
</Button>
|
||||
|
||||
<div style={{ cursor: 'pointer', padding: '10px 0' }} role="button" onClick={showAddStreamSourceDialog}>
|
||||
<FaFileImport size={30} style={{ verticalAlign: 'middle', marginRight: 5 }} /> {t('Include more tracks from other file')}
|
||||
</div>
|
||||
{externalFilesEntries.length === 0 && (
|
||||
<Button iconBefore={() => <ForkIcon size={16} />} onClick={onExtractAllStreamsPress}>
|
||||
{t('Export each track as individual files')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{nonCopiedExtraStreams.length > 0 && (
|
||||
<div style={{ margin: '10px 0' }}>
|
||||
{t('Discard or extract unprocessable tracks to separate files?')}
|
||||
<span style={{ marginRight: 10 }}>{t('Discard or extract unprocessable tracks to separate files?')}</span>
|
||||
<AutoExportToggler />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{externalFilesEntries.length === 0 && (
|
||||
<div style={{ cursor: 'pointer', padding: '10px 0' }} role="button" onClick={onExtractAllStreamsPress}>
|
||||
<FaFileExport size={30} style={{ verticalAlign: 'middle', marginRight: 5 }} /> {t('Export each track as individual files')}
|
||||
{externalFilesEntries.length > 0 && (
|
||||
<div style={{ margin: '10px 0' }}>
|
||||
<span style={{ marginRight: 10 }}>{t('When tracks have different lengths, do you want to make the output file as long as the longest or the shortest track?')}</span>
|
||||
<Button iconBefore={shortestFlag ? SortDescIcon : SortAscIcon} onClick={() => setShortestFlag((value) => !value)}>
|
||||
{shortestFlag ? t('Shortest') : t('Longest')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
import { IoIosHelpCircle, IoIosSettings } from 'react-icons/io';
|
||||
import { FaLock, FaUnlock } from 'react-icons/fa';
|
||||
import { IconButton, Button, CrossIcon } from 'evergreen-ui';
|
||||
import { IconButton, Button, CrossIcon, ListIcon, VolumeUpIcon, VolumeOffIcon } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import MergeExportButton from './components/MergeExportButton';
|
||||
@ -29,12 +29,12 @@ const TopMenu = memo(({
|
||||
<>
|
||||
{filePath && (
|
||||
<>
|
||||
<Button height={20} iconBefore="list" onClick={withBlur(() => setStreamsSelectorShown(true))}>
|
||||
<Button height={20} iconBefore={ListIcon} onClick={withBlur(() => setStreamsSelectorShown(true))}>
|
||||
{t('Tracks')} ({numStreamsToCopy}/{numStreamsTotal})
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
iconBefore={copyAnyAudioTrack ? 'volume-up' : 'volume-off'}
|
||||
iconBefore={copyAnyAudioTrack ? VolumeUpIcon : VolumeOffIcon}
|
||||
height={20}
|
||||
title={`${t('Discard audio? Current:')} ${copyAnyAudioTrack ? t('Keep audio tracks') : t('Discard audio tracks')}`}
|
||||
onClick={withBlur(toggleStripAudio)}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Button } from 'evergreen-ui';
|
||||
import { Button, FolderOpenIcon } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { withBlur } from '../util';
|
||||
@ -10,7 +10,7 @@ const CustomOutDirButton = memo(({ customOutDir, changeOutDir }) => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
iconBefore={customOutDir ? 'folder-open' : undefined}
|
||||
iconBefore={customOutDir ? FolderOpenIcon : undefined}
|
||||
height={20}
|
||||
onClick={withBlur(changeOutDir)}
|
||||
title={customOutDir}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Button } from 'evergreen-ui';
|
||||
import { Button, KeyIcon } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { withBlur } from '../util';
|
||||
@ -11,7 +11,7 @@ const KeyframeCutButton = memo(({ keyframeCut, onClick }) => {
|
||||
return (
|
||||
<Button
|
||||
height={20}
|
||||
iconBefore={keyframeCut ? 'key' : undefined}
|
||||
iconBefore={keyframeCut ? KeyIcon : undefined}
|
||||
title={`${t('Cut mode is:')} ${keyframeCut ? t('Keyframe cut') : t('Normal cut')}`}
|
||||
onClick={withBlur(onClick)}
|
||||
>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import App from './App';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import './i18n';
|
||||
@ -12,4 +13,11 @@ const electron = window.require('electron');
|
||||
|
||||
console.log('Version', electron.remote.app.getVersion());
|
||||
|
||||
ReactDOM.render(<ErrorBoundary><Suspense fallback={<div />}><App /></Suspense></ErrorBoundary>, document.getElementById('root'));
|
||||
|
||||
ReactDOM.render((
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<div />}>
|
||||
<App />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
), document.getElementById('root'));
|
||||
|
18
src/theme.js
Normal file
18
src/theme.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { defaultTheme } from 'evergreen-ui';
|
||||
|
||||
export default {
|
||||
...defaultTheme,
|
||||
components: {
|
||||
...defaultTheme.components,
|
||||
Select: {
|
||||
...defaultTheme.components.Select,
|
||||
appearances: {
|
||||
...defaultTheme.components.Select.appearances,
|
||||
default: {
|
||||
...defaultTheme.components.Select.appearances.default,
|
||||
backgroundColor: '#fff', // If not, selects will be invisible on dark background
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -85,6 +85,15 @@ export const errorToast = (title) => toast.fire({
|
||||
title,
|
||||
});
|
||||
|
||||
export function handleError(error) {
|
||||
console.error('handleError', error);
|
||||
toast.fire({
|
||||
icon: 'error',
|
||||
text: i18n.t('An error has occurred. {{message}}', { message: error && typeof error.message === 'string' ? error.message.substr(0, 300) : '' }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const openDirToast = async ({ dirPath, ...props }) => {
|
||||
const { value } = await toast.fire({ icon: 'success', timer: 5000, showConfirmButton: true, confirmButtonText: i18n.t('Show'), showCancelButton: true, cancelButtonText: i18n.t('Close'), ...props });
|
||||
if (value) open(dirPath);
|
||||
|
191
yarn.lock
191
yarn.lock
@ -1118,14 +1118,6 @@
|
||||
core-js-pure "^3.0.0"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@7.0.0-beta.42":
|
||||
version "7.0.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.42.tgz#352e40c92e0460d3e82f49bd7e79f6cda76f919f"
|
||||
integrity sha512-iOGRzUoONLOtmCvjUsZv3mZzgCT6ljHQY5fr1qG1QIiJQwtM7zbPWGGpa3QWETq+UqwWyJnoi5XZDZRwZDFciQ==
|
||||
dependencies:
|
||||
core-js "^2.5.3"
|
||||
regenerator-runtime "^0.11.1"
|
||||
|
||||
"@babel/runtime@7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740"
|
||||
@ -1133,13 +1125,20 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.13.6", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.13.6", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
||||
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.8.7":
|
||||
version "7.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
|
||||
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.3.3":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
|
||||
@ -1177,14 +1176,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@blueprintjs/icons@^3.2.0":
|
||||
version "3.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.26.0.tgz#d3f0aa0d35a9ebe8648db9651af364dfcc529d9f"
|
||||
integrity sha512-1+yhYH1Fjj5qGx8drZUL2L1R42MiN0WVHTTKYqGEV9TAzhvFHCSZgALD7WNQa+FEibw/8B4U+79IRgUPJNEjow==
|
||||
dependencies:
|
||||
classnames "^2.2"
|
||||
tslib "~1.13.0"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
|
||||
@ -1603,6 +1594,13 @@
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@segment/react-tiny-virtual-list@^2.2.1":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@segment/react-tiny-virtual-list/-/react-tiny-virtual-list-2.2.1.tgz#658da8a93cfb83537235c89307818d6f1741aeb3"
|
||||
integrity sha512-G01b9DrsQLF+8yFRyyJeZBZkzbFuqILG9C7SFFS+GtTFbjdprkHt0CL0riCPOZfe1ZXiT8z8MaKoWfNhrfUjhQ==
|
||||
dependencies:
|
||||
prop-types "^15.5.7"
|
||||
|
||||
"@sideway/address@^4.1.0":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.1.tgz#9e321e74310963fdf8eebfbee09c7bd69972de4d"
|
||||
@ -1969,6 +1967,22 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
|
||||
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
|
||||
|
||||
"@types/react-transition-group@^4.4.0":
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.2.tgz#38890fd9db68bf1f2252b99a942998dc7877c5b3"
|
||||
integrity sha512-KibDWL6nshuOJ0fu8ll7QnV/LVTo3PzQ9aCPnRUYPfX7eZohHwLIdNHj7pftanREzHNP4/nJa8oeM73uSiavMQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "17.0.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.19.tgz#8f2a85e8180a43b57966b237d26a29481dacc991"
|
||||
integrity sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@^16.9.5":
|
||||
version "16.14.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.5.tgz#2c39b5cadefaf4829818f9219e5e093325979f4d"
|
||||
@ -3706,7 +3720,7 @@ class-utils@^0.3.5:
|
||||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@^2.2, classnames@^2.2.6:
|
||||
classnames@^2.2.6:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.0.tgz#19524334bad47ccd99793936b67f9be0860fe835"
|
||||
integrity sha512-UUf/S3eeczXBjHPpSnrZ1ZyxH3KmLW8nVYFUWIZA/dixYMIQr7l94yYKxaAkmPk7HO9dlT6gFqAPZC02tTdfQw==
|
||||
@ -3984,7 +3998,7 @@ compression@^1.7.4:
|
||||
safe-buffer "5.1.2"
|
||||
vary "~1.1.2"
|
||||
|
||||
compute-scroll-into-view@^1.0.17, compute-scroll-into-view@^1.0.9:
|
||||
compute-scroll-into-view@^1.0.14, compute-scroll-into-view@^1.0.17:
|
||||
version "1.0.17"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
|
||||
integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
|
||||
@ -4156,7 +4170,7 @@ core-js@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.3:
|
||||
core-js@^2.4.0:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
@ -4890,12 +4904,13 @@ dom-converter@^0.2:
|
||||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-helpers@^3.2.1, dom-helpers@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
|
||||
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.2.2"
|
||||
@ -4967,15 +4982,15 @@ dotenv@8.2.0, dotenv@^8.2.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||
|
||||
downshift@^3.3.1:
|
||||
version "3.4.8"
|
||||
resolved "https://registry.yarnpkg.com/downshift/-/downshift-3.4.8.tgz#06b7ad9e9c423a58e8a9049b2a00a5d19c7ef954"
|
||||
integrity sha512-dZL3iNL/LbpHNzUQAaVq/eTD1ocnGKKjbAl/848Q0KEp6t81LJbS37w3f93oD6gqqAnjdgM7Use36qZSipHXBw==
|
||||
downshift@^5.2.0:
|
||||
version "5.4.7"
|
||||
resolved "https://registry.yarnpkg.com/downshift/-/downshift-5.4.7.tgz#2ab7b0512cad33011ee6f29630f9a7bb74dff2b5"
|
||||
integrity sha512-xaH0RNqwJ5pAsyk9qBmR9XJWmg1OOWMfrhzYv0NH2NjJxn77S3zBcfClw341UfhGyKg5v+qVqg/CQzvAgBNCXQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.5"
|
||||
compute-scroll-into-view "^1.0.9"
|
||||
"@babel/runtime" "^7.10.2"
|
||||
compute-scroll-into-view "^1.0.14"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
react-is "^16.13.1"
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
@ -5640,29 +5655,27 @@ eventsource@^1.0.7:
|
||||
dependencies:
|
||||
original "^1.0.0"
|
||||
|
||||
evergreen-ui@^4.23.0:
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/evergreen-ui/-/evergreen-ui-4.29.1.tgz#6b79d9b38504c75be5c68f4ed53ad50598650f43"
|
||||
integrity sha512-STGuq7KqwTMa4/REEBXIqilpL04TJ5nRWa0UmFCFVQ+aWK/iIrxtqdu0jlfGRaJStmMPlwO/gTPA0KIS5DK5fA==
|
||||
evergreen-ui@^6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/evergreen-ui/-/evergreen-ui-6.4.0.tgz#5a0a88af3ca6ecce235d48edbf855f2ccfa07582"
|
||||
integrity sha512-dRNCx2Of2Sex30FcBzmOJA+egBLINWPIu2hm2KI5F3bpLsswvLfi3tIfNXN5RWxEK94RUguPgFLUlFcP5ExYSQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
"@blueprintjs/icons" "^3.2.0"
|
||||
"@segment/react-tiny-virtual-list" "^2.2.1"
|
||||
"@types/react" "^16.9.5"
|
||||
"@types/react-transition-group" "^4.4.0"
|
||||
arrify "^1.0.1"
|
||||
classnames "^2.2.6"
|
||||
dom-helpers "^3.2.1"
|
||||
downshift "^3.3.1"
|
||||
downshift "^5.2.0"
|
||||
fuzzaldrin-plus "^0.6.0"
|
||||
glamor "^2.20.40"
|
||||
lodash.debounce "^4.0.8"
|
||||
lodash.startcase "^4.4.0"
|
||||
lodash.merge "^4.6.2"
|
||||
prop-types "^15.6.2"
|
||||
react-is "^16.13.1"
|
||||
react-scrollbar-size "^2.0.2"
|
||||
react-tiny-virtual-list "^2.1.4"
|
||||
react-transition-group "^2.5.0"
|
||||
react-fast-compare "^3.2.0"
|
||||
react-transition-group "^4.4.1"
|
||||
tinycolor2 "^1.4.1"
|
||||
ui-box "^3.2.0"
|
||||
ui-box "^5.0.0"
|
||||
|
||||
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
|
||||
version "1.0.3"
|
||||
@ -5932,7 +5945,7 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "2.1.1"
|
||||
|
||||
fbjs@^0.8.12, fbjs@^0.8.16:
|
||||
fbjs@^0.8.12:
|
||||
version "0.8.17"
|
||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
|
||||
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
|
||||
@ -8706,10 +8719,10 @@ lodash.memoize@^4.1.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.startcase@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
|
||||
integrity sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.template@^4.5.0:
|
||||
version "4.5.0"
|
||||
@ -10899,7 +10912,7 @@ prompts@^2.0.1:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
@ -11148,15 +11161,10 @@ react-error-overlay@^6.0.9:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
||||
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
|
||||
|
||||
react-event-listener@^0.5.1:
|
||||
version "0.5.10"
|
||||
resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.5.10.tgz#378403c555fe616f312891507a742ecbbe2c90de"
|
||||
integrity sha512-YZklRszh9hq3WP3bdNLjFwJcTCVe7qyTf5+LWNaHfZQaZrptsefDK2B5HHpOsEEaMHvjllUPr0+qIFVTSsurow==
|
||||
dependencies:
|
||||
"@babel/runtime" "7.0.0-beta.42"
|
||||
fbjs "^0.8.16"
|
||||
prop-types "^15.6.0"
|
||||
warning "^3.0.0"
|
||||
react-fast-compare@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
|
||||
|
||||
react-hammerjs@^1.0.1:
|
||||
version "1.0.1"
|
||||
@ -11178,7 +11186,7 @@ react-icons@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0"
|
||||
integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.8.1, react-is@^16.9.0:
|
||||
react-is@^16.13.1, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
@ -11188,11 +11196,6 @@ react-is@^17.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-lottie-player@^1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/react-lottie-player/-/react-lottie-player-1.3.3.tgz#e44c635718d5cde973d4720fe8359523f0e832ba"
|
||||
@ -11273,16 +11276,6 @@ react-scripts@^4.0.3:
|
||||
optionalDependencies:
|
||||
fsevents "^2.1.3"
|
||||
|
||||
react-scrollbar-size@^2.0.2:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-scrollbar-size/-/react-scrollbar-size-2.1.0.tgz#105e797135cab92b1f9e16f00071db7f29f80754"
|
||||
integrity sha512-9dDUJvk7S48r0TRKjlKJ9e/LkLLYgc9LdQR6W21I8ZqtSrEsedPOoMji4nU3DHy7fx2l8YMScJS/N7qiloYzXQ==
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
prop-types "^15.6.0"
|
||||
react-event-listener "^0.5.1"
|
||||
stifle "^1.0.2"
|
||||
|
||||
react-sortable-hoc@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
|
||||
@ -11311,22 +11304,15 @@ react-syntax-highlighter@^15.4.3:
|
||||
prismjs "^1.22.0"
|
||||
refractor "^3.2.0"
|
||||
|
||||
react-tiny-virtual-list@^2.1.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-tiny-virtual-list/-/react-tiny-virtual-list-2.2.0.tgz#eafb6fcf764e4ed41150ff9752cdaad8b35edf4a"
|
||||
integrity sha512-MDiy2xyqfvkWrRiQNdHFdm36lfxmcLLKuYnUqcf9xIubML85cmYCgzBJrDsLNZ3uJQ5LEHH9BnxGKKSm8+C0Bw==
|
||||
react-transition-group@^4.4.1:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
|
||||
integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
|
||||
dependencies:
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react-transition-group@^2.5.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
|
||||
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
|
||||
dependencies:
|
||||
dom-helpers "^3.4.0"
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react-use@^13.26.1:
|
||||
version "13.27.1"
|
||||
@ -11493,7 +11479,7 @@ regenerate@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
||||
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
|
||||
|
||||
regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
@ -12619,11 +12605,6 @@ stealthy-require@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
||||
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
|
||||
|
||||
stifle@^1.0.2:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/stifle/-/stifle-1.1.1.tgz#4e4c565f19dcf9a6efa3a7379a70c42179edb8d6"
|
||||
integrity sha512-INvON4DXLAWxpor+f0ZHnYQYXBqDXQRW1znLpf5/C/AWzJ0eQQAThfdqHQ5BDkiyywD67rQGvbE4LC+Aig6K/Q==
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
|
||||
@ -13364,11 +13345,6 @@ tslib@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||
|
||||
tslib@~1.13.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
||||
|
||||
tsutils@^3.17.1:
|
||||
version "3.21.0"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
||||
@ -13487,10 +13463,10 @@ ua-parser-js@^0.7.18:
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.26.tgz#b3731860e241419abd5b542b1a0881070d92e0ce"
|
||||
integrity sha512-VwIvGlFNmpKbjzRt51jpbbFTrKIEgGHxIwA8Y69K1Bqc6bTIV7TaGGABOkghSFQWsLmcRB4drGvpfv9z2szqoQ==
|
||||
|
||||
ui-box@^3.2.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/ui-box/-/ui-box-3.3.1.tgz#39733d55507053ac2bc3fd1fc184d0d648ff682d"
|
||||
integrity sha512-bzaFMdtzeSz1YcMRjE1A5a1/ExRWVSu5c42f9YeDAhlVfgNSGCrQMEEAiq3XvEVKlVgnf6GxDfGrvzBZA3w2GA==
|
||||
ui-box@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ui-box/-/ui-box-5.0.0.tgz#8b73e81cc7a50cf53749b969d411e8f56a93af48"
|
||||
integrity sha512-DJZZKe8UoB1e6hNenNniO3ror9ER57EG5k+fOUm17vrLnU2T4vhoTnWGoStewXJzQ4Ur9ox9Xk4kljVKFpXlzQ==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.7.1"
|
||||
inline-style-prefixer "^5.0.4"
|
||||
@ -13921,13 +13897,6 @@ walker@^1.0.7, walker@~1.0.5:
|
||||
dependencies:
|
||||
makeerror "1.0.x"
|
||||
|
||||
warning@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
|
||||
integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watchpack-chokidar2@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
||||
|
Loading…
Reference in New Issue
Block a user