1
0
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:
Mikael Finstad 2021-08-25 22:21:30 +07:00
parent beb529fcbf
commit e688f151b6
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
12 changed files with 464 additions and 468 deletions

View File

@ -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",

View File

@ -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>
);
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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)}

View File

@ -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}

View File

@ -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)}
>

View File

@ -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
View 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
},
},
},
},
};

View File

@ -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
View File

@ -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"