mirror of
https://github.com/mifi/lossless-cut.git
synced 2026-09-24 16:00:30 +08:00
improve dark mode #1969
This commit is contained in:
+1
-2
@@ -41,9 +41,8 @@
|
||||
"devDependencies": {
|
||||
"@adamscybot/react-leaflet-component-marker": "^2.0.0",
|
||||
"@fontsource/open-sans": "^4.5.14",
|
||||
"@radix-ui/colors": "1",
|
||||
"@radix-ui/colors": "^3.0.0",
|
||||
"@radix-ui/react-checkbox": "^1.2.3",
|
||||
"@radix-ui/react-dialog": "^1.1.11",
|
||||
"@radix-ui/react-switch": "^1.2.2",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
|
||||
@@ -7,8 +7,8 @@ import { ReactSortable } from 'react-sortablejs';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { Dialog } from 'evergreen-ui';
|
||||
|
||||
import Dialog, { ConfirmButton } from './components/Dialog';
|
||||
import Swal from './swal';
|
||||
import useContextMenu from './hooks/useContextMenu';
|
||||
import useUserSettings from './hooks/useUserSettings';
|
||||
@@ -458,21 +458,19 @@ function SegmentList({
|
||||
onSegmentTagsCloseComplete();
|
||||
}, [editingSegmentTags, editingSegmentTagsSegmentIndex, onSegmentTagsCloseComplete, updateSegAtIndex]);
|
||||
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={t('Edit segment tags')}
|
||||
isShown={editingSegmentTagsSegmentIndex != null}
|
||||
hasCancel={false}
|
||||
isConfirmDisabled={editingTag != null}
|
||||
confirmLabel={t('Save')}
|
||||
onConfirm={onSegmentTagsConfirm}
|
||||
onCloseComplete={onSegmentTagsCloseComplete}
|
||||
>
|
||||
<div style={{ color: 'black' }}>
|
||||
<TagEditor customTags={editingSegmentTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add segment tag')} addTagText={t('Enter tag key')} />
|
||||
</div>
|
||||
</Dialog>
|
||||
{editingSegmentTagsSegmentIndex != null && (
|
||||
<Dialog ref={dialogRef} autoOpen onClose={onSegmentTagsCloseComplete} style={{ width: '100%', maxWidth: '40em' }}>
|
||||
<h1 style={{ marginTop: 0 }}>{t('Edit segment tags')}</h1>
|
||||
|
||||
<TagEditor customTags={editingSegmentTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add segment tag')} />
|
||||
|
||||
<ConfirmButton onClick={onSegmentTagsConfirm} disabled={editingTag != null}><FaSave style={{ verticalAlign: 'baseline', fontSize: '.8em', marginRight: '.3em' }} />{t('Save')}</ConfirmButton>
|
||||
</Dialog>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
style={{ width, background: controlsBackground, borderLeft: '1px solid var(--gray-7)', color: 'var(--gray-11)', transition: darkModeTransition, display: 'flex', flexDirection: 'column', overflowY: 'hidden' }}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { memo, useState, useMemo, useCallback, Dispatch, SetStateAction, CSSProperties, ReactNode, ChangeEventHandler } from 'react';
|
||||
import { memo, useState, useMemo, useCallback, Dispatch, SetStateAction, CSSProperties, ReactNode, ChangeEventHandler, useRef } from 'react';
|
||||
|
||||
import { FaImage, FaCheckCircle, FaPaperclip, FaVideo, FaVideoSlash, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaFileExport } from 'react-icons/fa';
|
||||
import { GoFileBinary } from 'react-icons/go';
|
||||
import { MdSubtitles } from 'react-icons/md';
|
||||
import { Checkbox, BookIcon, MoreIcon, Position, Popover, Menu, TrashIcon, EditIcon, InfoSignIcon, IconButton, Heading, SortAscIcon, SortDescIcon, Dialog, ForkIcon, WarningSignIcon } from 'evergreen-ui';
|
||||
import { BookIcon, MoreIcon, Position, Popover, Menu, TrashIcon, EditIcon, InfoSignIcon, IconButton, SortAscIcon, SortDescIcon, ForkIcon, WarningSignIcon } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
|
||||
import Dialog from './components/Dialog';
|
||||
import AutoExportToggler from './components/AutoExportToggler';
|
||||
import Select from './components/Select';
|
||||
import { showJson5Dialog } from './dialogs';
|
||||
@@ -19,6 +20,7 @@ import { CustomTagsByFile, FilesMeta, FormatTimecode, ParamsByStreamId, StreamPa
|
||||
import useUserSettings from './hooks/useUserSettings';
|
||||
import tryShowGpsMap from './gps';
|
||||
import Button from './components/Button';
|
||||
import Checkbox from './components/Checkbox';
|
||||
|
||||
|
||||
const dispositionOptions = ['default', 'dub', 'original', 'comment', 'lyrics', 'karaoke', 'forced', 'hearing_impaired', 'visual_impaired', 'clean_effects', 'attached_pic', 'captions', 'descriptions', 'dependent', 'metadata'];
|
||||
@@ -58,7 +60,7 @@ const EditFileDialog = memo(({ editingFile, allFilesMeta, customTagsByFile, setC
|
||||
});
|
||||
}, [editingFile, setCustomTagsByFile]);
|
||||
|
||||
return <TagEditor existingTags={existingTags} customTags={customTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add metadata')} addTagText={t('Enter metadata key')} />;
|
||||
return <TagEditor existingTags={existingTags} customTags={customTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add metadata')} />;
|
||||
});
|
||||
|
||||
const getStreamDispositionsObj = (stream: FFprobeStream) => ((stream && stream.disposition) || {});
|
||||
@@ -82,13 +84,13 @@ function StreamParametersEditor({ stream, streamParams, updateStreamParams }: {
|
||||
if (stream.codec_name === 'h264') {
|
||||
ui.push(
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
<Checkbox key="bsfH264Mp4toannexb" checked={!!streamParams.bsfH264Mp4toannexb} label={t('Enable "{{filterName}}" bitstream filter.', { filterName: 'h264_mp4toannexb' })} onChange={(e) => updateStreamParams((params) => { params.bsfH264Mp4toannexb = e.target.checked; })} />,
|
||||
<Checkbox key="bsfH264Mp4toannexb" checked={!!streamParams.bsfH264Mp4toannexb} label={t('Enable "{{filterName}}" bitstream filter.', { filterName: 'h264_mp4toannexb' })} onCheckedChange={(checked) => updateStreamParams((params) => { params.bsfH264Mp4toannexb = checked === true; })} />,
|
||||
);
|
||||
}
|
||||
if (stream.codec_name === 'hevc') {
|
||||
ui.push(
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
<Checkbox key="bsfHevcMp4toannexb" checked={!!streamParams.bsfHevcMp4toannexb} label={t('Enable "{{filterName}}" bitstream filter.', { filterName: 'hevc_mp4toannexb' })} onChange={(e) => updateStreamParams((params) => { params.bsfHevcMp4toannexb = e.target.checked; })} />,
|
||||
<Checkbox key="bsfHevcMp4toannexb" checked={!!streamParams.bsfHevcMp4toannexb} label={t('Enable "{{filterName}}" bitstream filter.', { filterName: 'hevc_mp4toannexb' })} onCheckedChange={(checked) => updateStreamParams((params) => { params.bsfHevcMp4toannexb = checked === true; })} />,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,21 +142,19 @@ const EditStreamDialog = memo(({ editingStream: { streamId: editingStreamId, pat
|
||||
});
|
||||
}, [editingFile, editingStreamId, updateStreamParams]);
|
||||
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
if (!editingStream) return null;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Edit track {{trackNum}} metadata', { trackNum: editingStream && (editingStream.index + 1) })}
|
||||
isShown={!!editingStream}
|
||||
hasCancel={false}
|
||||
isConfirmDisabled={editingTag != null}
|
||||
confirmLabel={t('Done')}
|
||||
onCloseComplete={() => setEditingStream(undefined)}
|
||||
>
|
||||
<div style={{ color: 'black' }}>
|
||||
<Heading>Parameters</Heading>
|
||||
{editingStream != null && <StreamParametersEditor stream={editingStream} streamParams={streamParams} updateStreamParams={(setter) => updateStreamParams(editingFile, editingStreamId, setter)} />}
|
||||
<Heading>Tags</Heading>
|
||||
<TagEditor existingTags={existingTags} customTags={customTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add metadata')} addTagText={t('Enter metadata key')} />
|
||||
</div>
|
||||
<Dialog ref={dialogRef} autoOpen onClose={() => setEditingStream(undefined)} style={{ maxWidth: '40em' }}>
|
||||
<h1 style={{ marginTop: 0 }}>{t('Edit track {{trackNum}} metadata', { trackNum: editingStream && (editingStream.index + 1) })}</h1>
|
||||
|
||||
<h2>Parameters</h2>
|
||||
{editingStream != null && <StreamParametersEditor stream={editingStream} streamParams={streamParams} updateStreamParams={(setter) => updateStreamParams(editingFile, editingStreamId, setter)} />}
|
||||
|
||||
<h2>Tags</h2>
|
||||
<TagEditor existingTags={existingTags} customTags={customTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add metadata')} />
|
||||
</Dialog>
|
||||
);
|
||||
});
|
||||
@@ -416,6 +416,8 @@ function StreamsSelector({
|
||||
showJson5Dialog({ title, json, darkMode });
|
||||
}, [darkMode]);
|
||||
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<p style={{ margin: '.5em 2em .5em 1em' }}>{t('Click to select which tracks to keep when exporting:')}</p>
|
||||
@@ -503,19 +505,13 @@ function StreamsSelector({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
title={t('Edit file metadata')}
|
||||
width="80%"
|
||||
isShown={editingFile != null}
|
||||
hasCancel={false}
|
||||
confirmLabel={t('Done')}
|
||||
onCloseComplete={() => setEditingFile(undefined)}
|
||||
isConfirmDisabled={editingTag != null}
|
||||
>
|
||||
<div style={{ color: 'black' }}>
|
||||
{editingFile != null && <EditFileDialog editingFile={editingFile} editingTag={editingTag} setEditingTag={setEditingTag} allFilesMeta={allFilesMeta} customTagsByFile={customTagsByFile} setCustomTagsByFile={setCustomTagsByFile} />}
|
||||
</div>
|
||||
</Dialog>
|
||||
{editingFile != null && (
|
||||
<Dialog ref={dialogRef} autoOpen onClose={() => setEditingFile(undefined)} style={{ maxWidth: '40em' }}>
|
||||
<h1 style={{ marginTop: 0 }}>{t('Edit file metadata')}</h1>
|
||||
|
||||
<EditFileDialog editingFile={editingFile} editingTag={editingTag} setEditingTag={setEditingTag} allFilesMeta={allFilesMeta} customTagsByFile={customTagsByFile} setCustomTagsByFile={setCustomTagsByFile} />
|
||||
</Dialog>
|
||||
)}
|
||||
|
||||
{editingStream != null && (
|
||||
<EditStreamDialog
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
|
||||
.button:disabled {
|
||||
opacity: .5;
|
||||
cursor: initial;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -2,8 +2,10 @@ import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef } from 'react';
|
||||
|
||||
import styles from './Button.module.css';
|
||||
|
||||
export type ButtonProps = DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const Button = forwardRef<HTMLButtonElement, DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>>(({ type = 'button', ...props }, ref) => (
|
||||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ type = 'button', ...props }, ref) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading, react/button-has-type
|
||||
<button ref={ref} className={styles['button']} type={type} {...props} />
|
||||
));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
}
|
||||
|
||||
.CheckboxRoot {
|
||||
flex-shrink: 0;
|
||||
background-color: var(--gray-8);
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useState, useCallback, useEffect, useMemo, CSSProperties } from 'react';
|
||||
import { memo, useState, useCallback, useEffect, useMemo, CSSProperties, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IconButton, Checkbox as EvergreenCheckbox, Dialog, Paragraph } from 'evergreen-ui';
|
||||
import { IconButton } from 'evergreen-ui';
|
||||
import { AiOutlineMergeCells } from 'react-icons/ai';
|
||||
import { FaQuestionCircle, FaExclamationTriangle, FaCog } from 'react-icons/fa';
|
||||
import i18n from 'i18next';
|
||||
@@ -19,6 +19,7 @@ import Sheet from './Sheet';
|
||||
import TextInput from './TextInput';
|
||||
import Button from './Button';
|
||||
import { defaultMergedFileTemplate, generateMergedFileNames, maxFileNameLength } from '../util/outputNameTemplate';
|
||||
import Dialog from './Dialog';
|
||||
|
||||
const { basename } = window.require('path');
|
||||
|
||||
@@ -196,6 +197,8 @@ function ConcatDialog({ isShown, onHide, paths, onConcat, alwaysConcatMultipleFi
|
||||
onConcat({ paths, includeAllStreams, streams: fileMeta!.streams, outFileName, fileFormat, clearBatchFilesAfterConcat });
|
||||
}, [clearBatchFilesAfterConcat, fileFormat, fileMeta, includeAllStreams, onConcat, outFileName, paths]);
|
||||
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet visible={isShown} onClosePress={onHide} maxWidth="100%" style={{ padding: '0 2em' }}>
|
||||
@@ -251,21 +254,25 @@ function ConcatDialog({ isShown, onHide, paths, onConcat, alwaysConcatMultipleFi
|
||||
)}
|
||||
</Sheet>
|
||||
|
||||
<Dialog isShown={settingsVisible} onCloseComplete={() => setSettingsVisible(false)} title={t('Merge options')} hasCancel={false} confirmLabel={t('Close')}>
|
||||
<EvergreenCheckbox checked={includeAllStreams} onChange={(e) => setIncludeAllStreams(e.target.checked)} label={`${t('Include all tracks?')} ${t('If this is checked, all audio/video/subtitle/data tracks will be included. This may not always work for all file types. If not checked, only default streams will be included.')}`} />
|
||||
{settingsVisible && (
|
||||
<Dialog ref={dialogRef} autoOpen onClose={() => setSettingsVisible(false)} style={{ maxWidth: '40em' }}>
|
||||
<h1 style={{ marginTop: 0 }}>{t('Merge options')}</h1>
|
||||
|
||||
<EvergreenCheckbox checked={preserveMetadataOnMerge} onChange={(e) => setPreserveMetadataOnMerge(e.target.checked)} label={t('Preserve original metadata when merging? (slow)')} />
|
||||
<Checkbox checked={includeAllStreams} onCheckedChange={(checked) => setIncludeAllStreams(checked === true)} label={`${t('Include all tracks?')} ${t('If this is checked, all audio/video/subtitle/data tracks will be included. This may not always work for all file types. If not checked, only default streams will be included.')}`} />
|
||||
|
||||
{fileFormat != null && isMov(fileFormat) && <EvergreenCheckbox checked={preserveMovData} onChange={(e) => setPreserveMovData(e.target.checked)} label={t('Preserve all MP4/MOV metadata?')} />}
|
||||
<Checkbox checked={preserveMetadataOnMerge} onCheckedChange={(checked) => setPreserveMetadataOnMerge(checked === true)} label={t('Preserve original metadata when merging? (slow)')} />
|
||||
|
||||
<EvergreenCheckbox checked={segmentsToChapters} onChange={(e) => setSegmentsToChapters(e.target.checked)} label={t('Create chapters from merged segments? (slow)')} />
|
||||
{fileFormat != null && isMov(fileFormat) && <Checkbox checked={preserveMovData} onCheckedChange={(checked) => setPreserveMovData(checked === true)} label={t('Preserve all MP4/MOV metadata?')} />}
|
||||
|
||||
<EvergreenCheckbox checked={alwaysConcatMultipleFiles} onChange={(e) => setAlwaysConcatMultipleFiles(e.target.checked)} label={t('Always open this dialog when opening multiple files')} />
|
||||
<Checkbox checked={segmentsToChapters} onCheckedChange={(checked) => setSegmentsToChapters(checked === true)} label={t('Create chapters from merged segments? (slow)')} />
|
||||
|
||||
<EvergreenCheckbox checked={clearBatchFilesAfterConcat} onChange={(e) => setClearBatchFilesAfterConcat(e.target.checked)} label={t('Clear batch file list after merge')} />
|
||||
<Checkbox checked={alwaysConcatMultipleFiles} onCheckedChange={(checked) => setAlwaysConcatMultipleFiles(checked === true)} label={t('Always open this dialog when opening multiple files')} />
|
||||
|
||||
<Paragraph>{t('Note that also other settings from the normal export dialog apply to this merge function. For more information about all options, see the export dialog.')}</Paragraph>
|
||||
</Dialog>
|
||||
<Checkbox checked={clearBatchFilesAfterConcat} onCheckedChange={(checked) => setClearBatchFilesAfterConcat(checked === true)} label={t('Clear batch file list after merge')} />
|
||||
|
||||
<p>{t('Note that also other settings from the normal export dialog apply to this merge function. For more information about all options, see the export dialog.')}</p>
|
||||
</Dialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, ReactNode, useCallback } from 'react';
|
||||
import { FaClipboard } from 'react-icons/fa';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MotionStyle, motion, useAnimation } from 'framer-motion';
|
||||
import i18n from '../i18n';
|
||||
|
||||
const electron = window.require('electron');
|
||||
const { clipboard } = electron;
|
||||
|
||||
function CopyClipboardButton({ text, style }: { text: string, style?: MotionStyle }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
function CopyClipboardButton({ text, style, children = ({ onClick }) => <FaClipboard title={i18n.t('Copy to clipboard')} onClick={onClick} /> }: {
|
||||
text: string,
|
||||
style?: MotionStyle,
|
||||
children?: (p: { onClick: () => void }) => ReactNode,
|
||||
}) {
|
||||
const animation = useAnimation();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
@@ -21,7 +23,7 @@ function CopyClipboardButton({ text, style }: { text: string, style?: MotionStyl
|
||||
|
||||
return (
|
||||
<motion.span animate={animation} style={{ display: 'inline-block', cursor: 'pointer', ...style }}>
|
||||
<FaClipboard title={t('Copy to clipboard')} onClick={onClick} />
|
||||
{children({ onClick })}
|
||||
</motion.span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
dialog::backdrop {
|
||||
background-color: var(--black-a7);
|
||||
animation: overlayShow 400ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
dialog {
|
||||
border: .1em solid var(--black-a3);
|
||||
background: var(--white-a8);
|
||||
color: var(--gray-12);
|
||||
backdrop-filter: blur(2em);
|
||||
border-radius: .5em;
|
||||
padding: 1.7em;
|
||||
animation: contentShow 200ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
:global(.dark-theme) dialog {
|
||||
border: .1em solid var(--white-a2);
|
||||
background: var(--black-a4);
|
||||
}
|
||||
|
||||
.close {
|
||||
all: unset;
|
||||
border-radius: 100%;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--gray-12);
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
background-color: var(--gray-3);
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
background-color: var(--gray-4);
|
||||
}
|
||||
|
||||
.close:focus {
|
||||
box-shadow: 0 0 0 .1em var(--gray-7);
|
||||
}
|
||||
|
||||
@keyframes overlayShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes contentShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(0, -5%) scale(0.96);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { DetailedHTMLProps, DialogHTMLAttributes, useCallback, useEffect, forwardRef } from 'react';
|
||||
|
||||
import styles from './Dialog.module.css';
|
||||
import Button, { ButtonProps } from './Button';
|
||||
import i18n from '../i18n';
|
||||
|
||||
|
||||
type Props = Omit<DetailedHTMLProps<DialogHTMLAttributes<HTMLDialogElement>, HTMLDialogElement>, 'open'> & {
|
||||
autoOpen?: boolean | undefined,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const Dialog = forwardRef<HTMLDialogElement, Props>(({ children, autoOpen, onClose, onClick, ...props }, ref) => {
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
if (autoOpen) (ref as React.RefObject<HTMLDialogElement>).current?.showModal();
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
else (ref as React.RefObject<HTMLDialogElement>).current?.close();
|
||||
}, [autoOpen, ref]);
|
||||
|
||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDialogElement>) => {
|
||||
if (!(ref != null && 'current' in ref && ref.current != null)) return;
|
||||
const dialogDimensions = ref.current.getBoundingClientRect();
|
||||
if (e.clientX < dialogDimensions.left
|
||||
|| e.clientX > dialogDimensions.right
|
||||
|| e.clientY < dialogDimensions.top
|
||||
|| e.clientY > dialogDimensions.bottom) {
|
||||
onClose?.(e);
|
||||
}
|
||||
onClick?.(e);
|
||||
}, [onClick, onClose, ref]);
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, react/jsx-props-no-spreading
|
||||
<dialog {...props} ref={ref} onClose={onClose} onClick={handleClick}>
|
||||
{children}
|
||||
|
||||
<form method="dialog">
|
||||
<Button type="submit" className={styles['close']} aria-label={i18n.t('Close')}>
|
||||
<FaTimes />
|
||||
</Button>
|
||||
</form>
|
||||
</dialog>
|
||||
);
|
||||
});
|
||||
|
||||
export const ConfirmButton = ({ style, ...props }: ButtonProps) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<Button style={{ fontSize: '1.2em', ...style }} {...props} />
|
||||
);
|
||||
|
||||
|
||||
export default Dialog;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, Fragment, useEffect, useMemo, useCallback, useState, ReactNode, SetStateAction, Dispatch, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SearchInput, PlusIcon, InlineAlert, UndoIcon, Paragraph, TakeActionIcon, IconButton, Button, DeleteIcon, AddIcon, Dialog } from 'evergreen-ui';
|
||||
import { SearchInput, PlusIcon, UndoIcon, TakeActionIcon, IconButton, Button, DeleteIcon, AddIcon } from 'evergreen-ui';
|
||||
import { FaMouse, FaPlus, FaStepForward, FaStepBackward } from 'react-icons/fa';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
@@ -8,7 +8,6 @@ import orderBy from 'lodash/orderBy';
|
||||
import uniq from 'lodash/uniq';
|
||||
|
||||
import useUserSettings from '../hooks/useUserSettings';
|
||||
import Swal from '../swal';
|
||||
import SetCutpointButton from './SetCutpointButton';
|
||||
import SegmentCutpointButton from './SegmentCutpointButton';
|
||||
import { getModifier } from '../hooks/useTimelineScroll';
|
||||
@@ -16,6 +15,7 @@ import { KeyBinding, KeyboardAction, ModifierKey } from '../../../../types';
|
||||
import { StateSegment } from '../types';
|
||||
import Sheet from './Sheet';
|
||||
import { splitKeyboardKeys } from '../util';
|
||||
import Dialog, { ConfirmButton } from './Dialog';
|
||||
|
||||
|
||||
type Category = string;
|
||||
@@ -92,32 +92,30 @@ const CreateBinding = memo(({
|
||||
|
||||
const isComboInvalid = validKeysDown.length === 0 && keysDown.length > 0;
|
||||
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
const fixedKeys = useMemo(() => fixKeys(keysDown), [keysDown]);
|
||||
|
||||
if (!isShown) return null;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Bind new key to action')}
|
||||
isShown={action != null}
|
||||
confirmLabel={t('Save')}
|
||||
cancelLabel={t('Cancel')}
|
||||
onCloseComplete={() => setCreatingBinding(undefined)}
|
||||
onConfirm={() => action != null && onNewKeyBindingConfirmed(action, keysDown)}
|
||||
onCancel={() => setCreatingBinding(undefined)}
|
||||
>
|
||||
{isShown ? (
|
||||
<div style={{ color: 'black' }}>
|
||||
<Paragraph marginBottom={10}><TakeActionIcon verticalAlign="middle" marginRight={5} /> {actionsMap[action].name} <span style={{ color: 'rgba(0,0,0,0.5)' }}>({action})</span></Paragraph>
|
||||
<Dialog ref={dialogRef} autoOpen onClose={() => setCreatingBinding(undefined)} style={{ maxWidth: '40em' }}>
|
||||
<h1 style={{ marginTop: 0 }}>{t('Bind new key to action')}</h1>
|
||||
|
||||
<Paragraph>{t('Please press your desired key combination. Make sure it doesn\'t conflict with any other binding or system hotkeys.')}</Paragraph>
|
||||
<p><TakeActionIcon verticalAlign="middle" marginRight={5} /> {actionsMap[action].name} <span style={{ color: 'var(--gray-10)' }}>({action})</span></p>
|
||||
|
||||
<div style={{ margin: '20px 0' }}>{renderKeys(validKeysDown.length > 0 ? validKeysDown : keysDown)}</div>
|
||||
<p>{t('Please press your desired key combination. Make sure it doesn\'t conflict with any other binding or system hotkeys.')}</p>
|
||||
|
||||
{isComboInvalid && <InlineAlert marginBottom={20} intent="danger">{t('Combination is invalid')}</InlineAlert>}
|
||||
<div style={{ margin: '20px 0' }}>{renderKeys(validKeysDown.length > 0 ? validKeysDown : keysDown)}</div>
|
||||
|
||||
<div>
|
||||
{!keysDown.includes('esc') && <Button iconBefore={PlusIcon} onClick={() => addKeyDown('esc')}>ESC</Button>}
|
||||
{keysDown.length > 0 && <Button intent="warning" iconBefore={UndoIcon} onClick={() => setKeysDown([])}>{t('Start over')}</Button>}
|
||||
</div>
|
||||
</div>
|
||||
) : <div />}
|
||||
{isComboInvalid && <p>{t('Combination is invalid')}</p>}
|
||||
|
||||
<div style={{ marginBottom: '1em' }}>
|
||||
{!keysDown.includes('esc') && <Button iconBefore={PlusIcon} onClick={() => addKeyDown('esc')}>ESC</Button>}
|
||||
{keysDown.length > 0 && <Button intent="warning" iconBefore={UndoIcon} onClick={() => setKeysDown([])}>{t('Start over')}</Button>}
|
||||
</div>
|
||||
|
||||
<ConfirmButton disabled={fixedKeys.length === 0 || isComboInvalid} onClick={() => action != null && onNewKeyBindingConfirmed(action, keysDown)}>{t('Save')}</ConfirmButton>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
||||
@@ -758,22 +756,15 @@ const KeyboardShortcuts = memo(({
|
||||
const stringifyKeys = (keys: string[]) => keys.join('+');
|
||||
|
||||
const onNewKeyBindingConfirmed = useCallback(async (action: KeyboardAction, keys: string[]) => {
|
||||
const fixedKeys = fixKeys(keys);
|
||||
if (fixedKeys.length === 0) return;
|
||||
const keysStr = stringifyKeys(fixedKeys);
|
||||
const keysStr = stringifyKeys(keys);
|
||||
console.log('new key binding', action, keysStr);
|
||||
|
||||
const duplicate = keyBindings.find((existingBinding) => existingBinding.keys === keysStr);
|
||||
let shouldReplaceDuplicate: KeyBinding | undefined;
|
||||
if (duplicate) {
|
||||
const { isConfirmed } = await Swal.fire({
|
||||
icon: 'warning',
|
||||
title: t('Duplicate keyboard combination'),
|
||||
text: t('Combination is already bound to "{{alreadyBoundKey}}". Do you want to replace the existing binding?', { alreadyBoundKey: actionsMap[duplicate.action]?.name }),
|
||||
confirmButtonText: t('Replace'),
|
||||
focusCancel: true,
|
||||
showCancelButton: true,
|
||||
});
|
||||
// eslint-disable-next-line no-alert
|
||||
const isConfirmed = window.confirm(t('Combination is already bound to "{{alreadyBoundKey}}". Do you want to replace the existing binding?', { alreadyBoundKey: actionsMap[duplicate.action]?.name }));
|
||||
|
||||
if (isConfirmed) {
|
||||
shouldReplaceDuplicate = duplicate;
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
:global(.dark-theme) .settings tr.header {
|
||||
background-color: var(--white-a6);
|
||||
background-color: var(--white-a3);
|
||||
}
|
||||
|
||||
.settings {
|
||||
|
||||
@@ -1,36 +1,50 @@
|
||||
import { memo, useRef, useState, useMemo, useCallback, useEffect, FormEventHandler, MouseEventHandler } from 'react';
|
||||
import { memo, useRef, useState, useMemo, useCallback, useEffect, FormEventHandler } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextInput, TrashIcon, ResetIcon, TickIcon, EditIcon, PlusIcon, Button, IconButton } from 'evergreen-ui';
|
||||
import { TrashIcon, ResetIcon, TickIcon, EditIcon, PlusIcon, IconButton } from 'evergreen-ui';
|
||||
import invariant from 'tiny-invariant';
|
||||
|
||||
import { askForMetadataKey } from '../dialogs';
|
||||
import { SegmentTags, segmentTagsSchema } from '../types';
|
||||
import CopyClipboardButton from './CopyClipboardButton';
|
||||
import { errorToast } from '../swal';
|
||||
import TextInput from './TextInput';
|
||||
import Button from './Button';
|
||||
|
||||
|
||||
const { clipboard } = window.require('electron');
|
||||
|
||||
const activeColor = '#429777';
|
||||
const activeColor = 'var(--gray-12)';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editingTag, setEditingTag, onTagsChange, onTagReset, addTagTitle, addTagText }: {
|
||||
existingTags?: SegmentTags, customTags?: SegmentTags | undefined, editingTag: string | undefined, setEditingTag: (v: string | undefined) => void, onTagsChange: (keyValues: Record<string, string>) => void, onTagReset: (tag: string) => void, addTagTitle: string, addTagText: string,
|
||||
function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editingTag, setEditingTag, onTagsChange, onTagReset, addTagTitle }: {
|
||||
existingTags?: SegmentTags,
|
||||
customTags?: SegmentTags | undefined,
|
||||
editingTag: string | undefined,
|
||||
setEditingTag: (v: string | undefined) => void,
|
||||
onTagsChange: (keyValues: Record<string, string>) => void,
|
||||
onTagReset: (tag: string) => void,
|
||||
addTagTitle: string,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [editingTagVal, setEditingTagVal] = useState<string>();
|
||||
const [newTag, setNewTag] = useState<string>();
|
||||
const [newTagKey, setNewTagKey] = useState<string>();
|
||||
const [newTagKeyInput, setNewTagKeyInput] = useState<string>('');
|
||||
|
||||
const mergedTags = useMemo(() => ({ ...existingTags, ...customTags, ...(newTag ? { [newTag]: '' } : undefined) }), [customTags, existingTags, newTag]);
|
||||
const newTagKeyInputError = useMemo(() => !!newTagKeyInput && newTagKeyInput.includes('='), [newTagKeyInput]);
|
||||
|
||||
const mergedTags = useMemo(() => ({
|
||||
...existingTags,
|
||||
...customTags,
|
||||
...(newTagKey && { [newTagKey]: '' }),
|
||||
}), [customTags, existingTags, newTagKey]);
|
||||
|
||||
const onResetClick = useCallback(() => {
|
||||
invariant(editingTag != null);
|
||||
onTagReset(editingTag);
|
||||
setEditingTag(undefined);
|
||||
setNewTag(undefined);
|
||||
setNewTagKey(undefined);
|
||||
}, [editingTag, onTagReset, setEditingTag]);
|
||||
|
||||
const onPasteClick = useCallback(async () => {
|
||||
@@ -53,9 +67,9 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
}, [editingTag, editingTagVal, onTagsChange, setEditingTag]);
|
||||
|
||||
const onEditClick = useCallback((tag?: string) => {
|
||||
if (newTag) {
|
||||
if (newTagKey) {
|
||||
saveTag();
|
||||
setNewTag(undefined);
|
||||
setNewTagKey(undefined);
|
||||
} else if (editingTag != null) {
|
||||
if (editingTagVal !== existingTags[editingTag]) {
|
||||
saveTag();
|
||||
@@ -66,28 +80,31 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
setEditingTag(tag);
|
||||
setEditingTagVal(tag && String(mergedTags[tag]));
|
||||
}
|
||||
}, [editingTag, editingTagVal, existingTags, mergedTags, newTag, onResetClick, saveTag, setEditingTag]);
|
||||
}, [editingTag, editingTagVal, existingTags, mergedTags, newTagKey, onResetClick, saveTag, setEditingTag]);
|
||||
|
||||
const onSubmit = useCallback<FormEventHandler<HTMLFormElement>>((e) => {
|
||||
e.preventDefault();
|
||||
onEditClick();
|
||||
}, [onEditClick]);
|
||||
|
||||
const onAddPress = useCallback<MouseEventHandler<HTMLButtonElement>>(async (e) => {
|
||||
e.preventDefault();
|
||||
(e.target as HTMLButtonElement).blur();
|
||||
|
||||
if (newTag || editingTag != null) {
|
||||
const add = useCallback(() => {
|
||||
if (newTagKey || editingTag != null) {
|
||||
// save any unsaved edit
|
||||
onEditClick();
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = await askForMetadataKey({ title: addTagTitle, text: addTagText });
|
||||
if (!tag || Object.keys(mergedTags).includes(tag)) return;
|
||||
setEditingTag(tag);
|
||||
if (!newTagKeyInput || newTagKeyInputError || Object.keys(mergedTags).includes(newTagKeyInput)) return;
|
||||
setEditingTag(newTagKeyInput);
|
||||
setEditingTagVal('');
|
||||
setNewTag(tag);
|
||||
}, [addTagText, addTagTitle, editingTag, mergedTags, newTag, onEditClick, setEditingTag]);
|
||||
setNewTagKey(newTagKeyInput);
|
||||
setNewTagKeyInput('');
|
||||
}, [editingTag, mergedTags, newTagKey, newTagKeyInput, newTagKeyInputError, onEditClick, setEditingTag]);
|
||||
|
||||
const onAddSubmit = useCallback<FormEventHandler<HTMLFormElement>>((e) => {
|
||||
e.preventDefault();
|
||||
add();
|
||||
}, [add]);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current?.focus();
|
||||
@@ -95,7 +112,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
|
||||
return (
|
||||
<>
|
||||
<table style={{ color: 'black', marginBottom: 10 }}>
|
||||
<table style={{ marginBottom: 10 }}>
|
||||
<tbody>
|
||||
{Object.keys(mergedTags).map((tag) => {
|
||||
const editingThis = tag === editingTag;
|
||||
@@ -105,15 +122,15 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
|
||||
return (
|
||||
<tr key={tag}>
|
||||
<td style={{ paddingRight: 20, color: thisTagNew ? activeColor : 'rgba(0,0,0,0.6)' }}>{tag}</td>
|
||||
<td style={{ paddingRight: '1em', color: thisTagNew ? activeColor : 'var(--gray-11)' }}>{tag}</td>
|
||||
|
||||
<td style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{editingThis ? (
|
||||
<form style={{ display: 'inline' }} onSubmit={onSubmit}>
|
||||
<TextInput ref={ref} placeholder={t('Enter value')} value={editingTagVal || ''} onChange={(e) => setEditingTagVal(e.target.value)} style={{ textTransform: editingTag === 'language' ? 'lowercase' : undefined }} />
|
||||
<TextInput ref={ref} placeholder={t('Enter value')} value={editingTagVal || ''} onChange={(e) => setEditingTagVal(e.target.value)} style={{ padding: '.4em', textTransform: editingTag === 'language' ? 'lowercase' : undefined }} />
|
||||
</form>
|
||||
) : (
|
||||
<span style={{ padding: '.5em 0', color: thisTagCustom ? activeColor : undefined, fontWeight: thisTagCustom ? 'bold' : undefined }}>{mergedTags[tag] ? String(mergedTags[tag]) : `<${t('empty')}>`}</span>
|
||||
<span style={{ padding: '.5em 0', color: thisTagCustom ? activeColor : 'var(--gray-11)', fontWeight: thisTagCustom ? 'bold' : undefined }}>{mergedTags[tag] ? String(mergedTags[tag]) : `<${t('empty')}>`}</span>
|
||||
)}
|
||||
{(editingTag == null || editingThis) && <IconButton icon={Icon} title={t('Edit')} appearance="minimal" style={{ marginLeft: '.4em' }} onClick={() => onEditClick(tag)} intent={editingThis ? 'success' : 'none'} />}
|
||||
{editingThis && <IconButton icon={thisTagNew ? TrashIcon : ResetIcon} title={thisTagNew ? t('Delete') : t('Reset')} appearance="minimal" onClick={onResetClick} intent="danger" />}
|
||||
@@ -124,13 +141,23 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Button iconBefore={PlusIcon} onClick={onAddPress}>{addTagTitle}</Button>
|
||||
{(!newTagKey && (editingTag == null || mergedTags[editingTag] == null)) && (
|
||||
<>
|
||||
<form onSubmit={onAddSubmit}>
|
||||
<TextInput value={newTagKeyInput} onChange={(e) => setNewTagKeyInput(e.target.value)} placeholder={addTagTitle} style={{ marginBottom: '1em', padding: '.4em', marginRight: '1em' }} />
|
||||
<Button type="button" title={addTagTitle} onClick={add} style={{ padding: '.3em' }}><PlusIcon style={{ verticalAlign: 'middle' }} /></Button>
|
||||
</form>
|
||||
|
||||
<div style={{ marginTop: '1em' }}>
|
||||
<span style={{ marginRight: '1em' }}>{t('Batch')}:</span>
|
||||
<CopyClipboardButton text={JSON.stringify(mergedTags)} style={{ marginRight: '.3em', verticalAlign: 'middle' }} />
|
||||
{newTagKeyInputError && <div>{t('Invalid character(s) found in key')}</div>}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button appearance="minimal" onClick={onPasteClick}>{t('Paste')}</Button>
|
||||
<div style={{ marginTop: '1em', marginBottom: '1em' }}>
|
||||
<CopyClipboardButton text={JSON.stringify(mergedTags, null, 2)} style={{ marginRight: '.3em', verticalAlign: 'middle' }}>
|
||||
{({ onClick }) => <Button onClick={onClick} style={{ display: 'block' }}>{t('Copy to clipboard')}</Button>}
|
||||
</CopyClipboardButton>
|
||||
|
||||
<Button onClick={onPasteClick}>{t('Paste')}</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -378,18 +378,6 @@ export async function askForAlignSegments() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function askForMetadataKey({ title, text }: { title: string, text: string }) {
|
||||
const { value } = await Swal.fire<string>({
|
||||
title,
|
||||
text,
|
||||
input: 'text',
|
||||
showCancelButton: true,
|
||||
inputPlaceholder: 'key',
|
||||
inputValidator: (v) => (v.includes('=') ? i18n.t('Invalid character(s) found in key') : null),
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
export async function confirmExtractAllStreamsDialog() {
|
||||
const { value } = await Swal.fire<string>({
|
||||
text: i18n.t('Please confirm that you want to extract all tracks as separate files'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import 'sweetalert2/src/variables';
|
||||
$swal2-outline-color: rgba(100, 150, 200, 0.5) !default;
|
||||
|
||||
// see colors.ts primaryColor
|
||||
$swal2-confirm-button-background-color: var(--cyan-9);
|
||||
|
||||
@@ -1506,130 +1506,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-dialog@npm:^1.1.11":
|
||||
version: 1.1.11
|
||||
resolution: "@radix-ui/react-dialog@npm:1.1.11"
|
||||
dependencies:
|
||||
"@radix-ui/primitive": "npm:1.1.2"
|
||||
"@radix-ui/react-compose-refs": "npm:1.1.2"
|
||||
"@radix-ui/react-context": "npm:1.1.2"
|
||||
"@radix-ui/react-dismissable-layer": "npm:1.1.7"
|
||||
"@radix-ui/react-focus-guards": "npm:1.1.2"
|
||||
"@radix-ui/react-focus-scope": "npm:1.1.4"
|
||||
"@radix-ui/react-id": "npm:1.1.1"
|
||||
"@radix-ui/react-portal": "npm:1.1.6"
|
||||
"@radix-ui/react-presence": "npm:1.1.4"
|
||||
"@radix-ui/react-primitive": "npm:2.1.0"
|
||||
"@radix-ui/react-slot": "npm:1.2.0"
|
||||
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
|
||||
aria-hidden: "npm:^1.2.4"
|
||||
react-remove-scroll: "npm:^2.6.3"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 10/04b8889fb2c29809468f2e83d31a39d5c2b12c2ac286c3cb70eb20dea82bd21b9711ddfd2f9885f3240675c91507771703c8dfc6083bf95cecdd80c99e16e8c5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-dismissable-layer@npm:1.1.7":
|
||||
version: 1.1.7
|
||||
resolution: "@radix-ui/react-dismissable-layer@npm:1.1.7"
|
||||
dependencies:
|
||||
"@radix-ui/primitive": "npm:1.1.2"
|
||||
"@radix-ui/react-compose-refs": "npm:1.1.2"
|
||||
"@radix-ui/react-primitive": "npm:2.1.0"
|
||||
"@radix-ui/react-use-callback-ref": "npm:1.1.1"
|
||||
"@radix-ui/react-use-escape-keydown": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 10/83e6116d932e30a8ee35aad0c9b25a0c5b31358e5d117679db10d7f2336fa9aac272a3c4c246075fea3e718f14d7dbbacc28605473f0e8274b519edb0c79be23
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-focus-guards@npm:1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "@radix-ui/react-focus-guards@npm:1.1.2"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/618658e2b98575198b94ccfdd27f41beb37f83721c9a04617e848afbc47461124ae008d703d713b9644771d96d4852e49de322cf4be3b5f10a4f94d200db5248
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-focus-scope@npm:1.1.4":
|
||||
version: 1.1.4
|
||||
resolution: "@radix-ui/react-focus-scope@npm:1.1.4"
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs": "npm:1.1.2"
|
||||
"@radix-ui/react-primitive": "npm:2.1.0"
|
||||
"@radix-ui/react-use-callback-ref": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 10/ba405724703e151b56a787ee8c8a259fb46f51d9e2ea755597bcebb9ff0b854ca591d9bd09d6b5168871d1150104b36342fd1f47e33b3759925d19800bd580a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-id@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@radix-ui/react-id@npm:1.1.1"
|
||||
dependencies:
|
||||
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/8d68e200778eb3038906870fc869b3d881f4a46715fb20cddd9c76cba42fdaaa4810a3365b6ec2daf0f185b9201fc99d009167f59c7921bc3a139722c2e976db
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-portal@npm:1.1.6":
|
||||
version: 1.1.6
|
||||
resolution: "@radix-ui/react-portal@npm:1.1.6"
|
||||
dependencies:
|
||||
"@radix-ui/react-primitive": "npm:2.1.0"
|
||||
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 10/a521c1ed242a404bee59d8fc881b3a22d6135cfa4bb34d63c13b141d37e1572e31b324f3f60f629415e61963a916bddd5ce00ef42a8175dc9f3cba968ed5d4c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-presence@npm:1.1.4":
|
||||
version: 1.1.4
|
||||
resolution: "@radix-ui/react-presence@npm:1.1.4"
|
||||
@@ -1709,19 +1585,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-use-callback-ref@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/cde8c40f1d4e79e6e71470218163a746858304bad03758ac84dc1f94247a046478e8e397518350c8d6609c84b7e78565441d7505bb3ed573afce82cfdcd19faf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-use-controllable-state@npm:1.2.2":
|
||||
version: 1.2.2
|
||||
resolution: "@radix-ui/react-use-controllable-state@npm:1.2.2"
|
||||
@@ -1753,21 +1616,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-use-escape-keydown@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@radix-ui/react-use-escape-keydown@npm:1.1.1"
|
||||
dependencies:
|
||||
"@radix-ui/react-use-callback-ref": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/0eb0756c2c55ddcde9ff01446ab01c085ab2bf799173e97db7ef5f85126f9e8600225570801a1f64740e6d14c39ffe8eed7c14d29737345a5797f4622ac96f6f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-use-layout-effect@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@radix-ui/react-use-layout-effect@npm:1.1.1"
|
||||
@@ -3004,15 +2852,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-hidden@npm:^1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "aria-hidden@npm:1.2.4"
|
||||
dependencies:
|
||||
tslib: "npm:^2.0.0"
|
||||
checksum: 10/df4bc15423aaaba3729a7d40abcbf6d3fffa5b8fd5eb33d3ac8b7da0110c47552fca60d97f2e1edfbb68a27cae1da499f1c3896966efb3e26aac4e3b57e3cc8b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-query@npm:^5.3.0":
|
||||
version: 5.3.0
|
||||
resolution: "aria-query@npm:5.3.0"
|
||||
@@ -4416,13 +4255,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-node-es@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "detect-node-es@npm:1.1.0"
|
||||
checksum: 10/e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-node@npm:^2.0.4":
|
||||
version: 2.1.0
|
||||
resolution: "detect-node@npm:2.1.0"
|
||||
@@ -6309,13 +6141,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-nonce@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "get-nonce@npm:1.0.1"
|
||||
checksum: 10/ad5104871d114a694ecc506a2d406e2331beccb961fe1e110dc25556b38bcdbf399a823a8a375976cd8889668156a9561e12ebe3fa6a4c6ba169c8466c2ff868
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^5.1.0":
|
||||
version: 5.2.0
|
||||
resolution: "get-stream@npm:5.2.0"
|
||||
@@ -7843,7 +7668,6 @@ __metadata:
|
||||
"@octokit/core": "npm:5"
|
||||
"@radix-ui/colors": "npm:^3.0.0"
|
||||
"@radix-ui/react-checkbox": "npm:^1.2.3"
|
||||
"@radix-ui/react-dialog": "npm:^1.1.11"
|
||||
"@radix-ui/react-switch": "npm:^1.2.2"
|
||||
"@tsconfig/node18": "npm:^18.2.2"
|
||||
"@tsconfig/node20": "npm:^20.1.4"
|
||||
@@ -9379,41 +9203,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-remove-scroll-bar@npm:^2.3.7":
|
||||
version: 2.3.8
|
||||
resolution: "react-remove-scroll-bar@npm:2.3.8"
|
||||
dependencies:
|
||||
react-style-singleton: "npm:^2.2.2"
|
||||
tslib: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/6c0f8cff98b9f49a4ee2263f1eedf12926dced5ce220fbe83bd93544460e2a7ec8ec39b35d1b2a75d2fced0b2d64afeb8e66f830431ca896e05a20585f9fc350
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-remove-scroll@npm:^2.6.3":
|
||||
version: 2.6.3
|
||||
resolution: "react-remove-scroll@npm:2.6.3"
|
||||
dependencies:
|
||||
react-remove-scroll-bar: "npm:^2.3.7"
|
||||
react-style-singleton: "npm:^2.2.3"
|
||||
tslib: "npm:^2.1.0"
|
||||
use-callback-ref: "npm:^1.3.3"
|
||||
use-sidecar: "npm:^1.1.3"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/d4dfd38e4381fa6059c8b810568b2d3a31fe21168bb3e2f57d1b1885ee08736fbd5a3fd83936faef0d17031c9c4175a1af83885bfc6c4280611f025447b19a4c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-reverse-portal@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "react-reverse-portal@npm:2.1.2"
|
||||
@@ -9439,22 +9228,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3":
|
||||
version: 2.2.3
|
||||
resolution: "react-style-singleton@npm:2.2.3"
|
||||
dependencies:
|
||||
get-nonce: "npm:^1.0.0"
|
||||
tslib: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/62498094ff3877a37f351b29e6cad9e38b2eb1ac3c0cb27ebf80aee96554f80b35e17bdb552bcd7ac8b7cb9904fea93ea5668f2057c73d38f90b5d46bb9b27ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-syntax-highlighter@npm:^15.4.3":
|
||||
version: 15.4.5
|
||||
resolution: "react-syntax-highlighter@npm:15.4.5"
|
||||
@@ -10819,9 +10592,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"sweetalert2@npm:^11.11.0":
|
||||
version: 11.11.0
|
||||
resolution: "sweetalert2@npm:11.11.0"
|
||||
checksum: 10/8a5ead894f4e15b9f74a7bb95077972b85c92367dd157401bfe7b4cd933382d23640f634105fc984760c0ed02a9910f4a54a357eaebd999cbac3cd10e1f03237
|
||||
version: 11.21.0
|
||||
resolution: "sweetalert2@npm:11.21.0"
|
||||
checksum: 10/97d216b459a5e6502e179cc37aacc47885b6d76b2b0930109b3d55b44fe6f072549974b43045ce684b9107bf737c37fc702a6b82cd3fd01cee128ac017057def
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -11135,20 +10908,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^2.0.0":
|
||||
"tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0":
|
||||
version: 2.8.1
|
||||
resolution: "tslib@npm:2.8.1"
|
||||
checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0":
|
||||
version: 2.5.0
|
||||
resolution: "tslib@npm:2.5.0"
|
||||
checksum: 10/ea556fbdf396fe15dbd45e242754e86e7c36e0dce8644404a7c8a81ae1e940744dc639569aeca1ae370a7f804d82872f3fd8564eb23be9adb7618201d0314dac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsx@npm:^4.7.1":
|
||||
version: 4.7.1
|
||||
resolution: "tsx@npm:4.7.1"
|
||||
@@ -11489,21 +11255,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"use-callback-ref@npm:^1.3.3":
|
||||
version: 1.3.3
|
||||
resolution: "use-callback-ref@npm:1.3.3"
|
||||
dependencies:
|
||||
tslib: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/adf06a7b6a27d3651c325ac9b66d2b82ccacaed7450b85b211d123e91d9a23cb5a587fcc6db5b4fd07ac7233e5abf024d30cf02ddc2ec46bca712151c0836151
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"use-debounce@npm:^5.1.0":
|
||||
version: 5.2.1
|
||||
resolution: "use-debounce@npm:5.2.1"
|
||||
@@ -11513,22 +11264,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"use-sidecar@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "use-sidecar@npm:1.1.3"
|
||||
dependencies:
|
||||
detect-node-es: "npm:^1.1.0"
|
||||
tslib: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/2fec05eb851cdfc4a4657b1dfb434e686f346c3265ffc9db8a974bb58f8128bd4a708a3cc00e8f51655fccf81822ed4419ebed42f41610589e3aab0cf2492edb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"use-trace-update@npm:^1.3.0":
|
||||
version: 1.3.2
|
||||
resolution: "use-trace-update@npm:1.3.2"
|
||||
|
||||
Reference in New Issue
Block a user