mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 10:22:31 +01:00
parent
8217be4f14
commit
3f4c214287
@ -136,7 +136,8 @@
|
||||
"semver": "^7.5.2",
|
||||
"string-to-stream": "^3.0.1",
|
||||
"winston": "^3.8.1",
|
||||
"yargs-parser": "^21.1.1"
|
||||
"yargs-parser": "^21.1.1",
|
||||
"zod": "^3.22.5"
|
||||
},
|
||||
"build": {
|
||||
"directories": {
|
||||
|
@ -400,11 +400,11 @@ const SegmentList = memo(({
|
||||
);
|
||||
}
|
||||
|
||||
const [editingTag, setEditingTag] = useState();
|
||||
const [editingTag, setEditingTag] = useState<string>();
|
||||
|
||||
const onTagChange = useCallback((tag: string, value: string) => setEditingSegmentTags((existingTags) => ({
|
||||
const onTagsChange = useCallback((keyValues: Record<string, string>) => setEditingSegmentTags((existingTags) => ({
|
||||
...existingTags,
|
||||
[tag]: value,
|
||||
...keyValues,
|
||||
})), [setEditingSegmentTags]);
|
||||
|
||||
const onTagReset = useCallback((tag: string) => setEditingSegmentTags((tags) => {
|
||||
@ -437,7 +437,7 @@ const SegmentList = memo(({
|
||||
onCloseComplete={onSegmentTagsCloseComplete}
|
||||
>
|
||||
<div style={{ color: 'black' }}>
|
||||
<TagEditor customTags={editingSegmentTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagChange={onTagChange} onTagReset={onTagReset} addTagTitle={t('Add segment tag')} addTagText={t('Enter tag key')} />
|
||||
<TagEditor customTags={editingSegmentTags} editingTag={editingTag} setEditingTag={setEditingTag} onTagsChange={onTagsChange} onTagReset={onTagReset} addTagTitle={t('Add segment tag')} addTagText={t('Enter tag key')} />
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
|
@ -23,7 +23,6 @@ const CopyClipboardButton = memo(({ text, style }: { text: string, style?: Motio
|
||||
<motion.span animate={animation} style={{ display: 'inline-block', cursor: 'pointer', ...style }}>
|
||||
<FaClipboard title={t('Copy to clipboard')} onClick={onClick} />
|
||||
</motion.span>
|
||||
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,46 +1,70 @@
|
||||
import { memo, useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextInput, TrashIcon, TickIcon, EditIcon, PlusIcon, Button, 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';
|
||||
|
||||
|
||||
const { clipboard } = window.require('electron');
|
||||
|
||||
const activeColor = '#429777';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editingTag, setEditingTag, onTagChange, onTagReset, addTagTitle, addTagText }) {
|
||||
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,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef();
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [editingTagVal, setEditingTagVal] = useState();
|
||||
const [newTag, setNewTag] = useState();
|
||||
const [editingTagVal, setEditingTagVal] = useState<string>();
|
||||
const [newTag, setNewTag] = useState<string>();
|
||||
|
||||
const mergedTags = useMemo(() => ({ ...existingTags, ...customTags, ...(newTag ? { [newTag]: '' } : {}) }), [customTags, existingTags, newTag]);
|
||||
const mergedTags = useMemo(() => ({ ...existingTags, ...customTags, ...(newTag ? { [newTag]: '' } : undefined) }), [customTags, existingTags, newTag]);
|
||||
|
||||
const onResetClick = useCallback(() => {
|
||||
invariant(editingTag != null);
|
||||
onTagReset(editingTag);
|
||||
setEditingTag();
|
||||
setNewTag();
|
||||
setEditingTag(undefined);
|
||||
setNewTag(undefined);
|
||||
}, [editingTag, onTagReset, setEditingTag]);
|
||||
|
||||
const onEditClick = useCallback((tag) => {
|
||||
const onPasteClick = useCallback(async () => {
|
||||
const text = clipboard.readText();
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
const newTags = segmentTagsSchema.parse(json);
|
||||
onTagsChange(newTags);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) errorToast(e.message);
|
||||
}
|
||||
}, [onTagsChange]);
|
||||
|
||||
const onEditClick = useCallback((tag?: string) => {
|
||||
if (newTag) {
|
||||
onTagChange(editingTag, editingTagVal);
|
||||
setEditingTag();
|
||||
setNewTag();
|
||||
invariant(editingTag != null);
|
||||
invariant(editingTagVal != null);
|
||||
onTagsChange({ [editingTag]: editingTagVal });
|
||||
setEditingTag(undefined);
|
||||
setNewTag(undefined);
|
||||
} else if (editingTag != null) {
|
||||
if (editingTagVal !== existingTags[editingTag]) {
|
||||
onTagChange(editingTag, editingTagVal);
|
||||
setEditingTag();
|
||||
invariant(editingTag != null);
|
||||
invariant(editingTagVal != null);
|
||||
onTagsChange({ [editingTag]: editingTagVal });
|
||||
setEditingTag(undefined);
|
||||
} else { // If not actually changed, no need to update
|
||||
onResetClick();
|
||||
}
|
||||
} else {
|
||||
setEditingTag(tag);
|
||||
setEditingTagVal(mergedTags[tag]);
|
||||
setEditingTagVal(tag && String(mergedTags[tag]));
|
||||
}
|
||||
}, [editingTag, editingTagVal, existingTags, mergedTags, newTag, onResetClick, onTagChange, setEditingTag]);
|
||||
}, [editingTag, editingTagVal, existingTags, mergedTags, newTag, onResetClick, onTagsChange, setEditingTag]);
|
||||
|
||||
function onSubmit(e) {
|
||||
e.preventDefault();
|
||||
@ -69,7 +93,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
|
||||
return (
|
||||
<>
|
||||
<table style={{ color: 'black' }}>
|
||||
<table style={{ color: 'black', marginBottom: 10 }}>
|
||||
<tbody>
|
||||
{Object.keys(mergedTags).map((tag) => {
|
||||
const editingThis = tag === editingTag;
|
||||
@ -87,7 +111,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
<TextInput ref={ref} placeholder={t('Enter value')} value={editingTagVal || ''} onChange={(e) => setEditingTagVal(e.target.value)} />
|
||||
</form>
|
||||
) : (
|
||||
<span style={{ padding: '.5em 0', color: thisTagCustom ? activeColor : undefined, fontWeight: thisTagCustom ? 'bold' : undefined }}>{mergedTags[tag] || `<${t('empty')}>`}</span>
|
||||
<span style={{ padding: '.5em 0', color: thisTagCustom ? activeColor : undefined, 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={TrashIcon} title={thisTagCustom ? t('Delete') : t('Reset')} appearance="minimal" onClick={onResetClick} intent="danger" />}
|
||||
@ -98,7 +122,14 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Button style={{ marginTop: 10 }} iconBefore={PlusIcon} onClick={onAddPress}>{addTagTitle}</Button>
|
||||
<Button iconBefore={PlusIcon} onClick={onAddPress}>{addTagTitle}</Button>
|
||||
|
||||
<div style={{ marginTop: '1em' }}>
|
||||
<span style={{ marginRight: '1em' }}>{t('Batch')}:</span>
|
||||
<CopyClipboardButton text={JSON.stringify(mergedTags)} style={{ marginRight: '.3em', verticalAlign: 'middle' }} />
|
||||
|
||||
<Button appearance="minimal" onClick={onPasteClick}>{t('Paste')}</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -339,8 +339,8 @@ export async function askForAlignSegments() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function askForMetadataKey({ title, text }) {
|
||||
const { value } = await Swal.fire({
|
||||
export async function askForMetadataKey({ title, text }: { title: string, text: string }) {
|
||||
const { value } = await Swal.fire<string>({
|
||||
title,
|
||||
text,
|
||||
input: 'text',
|
||||
@ -352,7 +352,7 @@ export async function askForMetadataKey({ title, text }) {
|
||||
}
|
||||
|
||||
export async function confirmExtractAllStreamsDialog() {
|
||||
const { value } = await Swal.fire({
|
||||
const { value } = await Swal.fire<string>({
|
||||
text: i18n.t('Please confirm that you want to extract all tracks as separate files'),
|
||||
showCancelButton: true,
|
||||
confirmButtonText: i18n.t('Extract all tracks'),
|
||||
|
@ -49,7 +49,7 @@ export const swalToastOptions: SweetAlertOptions = {
|
||||
|
||||
export const toast = Swal.mixin(swalToastOptions);
|
||||
|
||||
export const errorToast = (text) => toast.fire({
|
||||
export const errorToast = (text: string) => toast.fire({
|
||||
icon: 'error',
|
||||
text,
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { MenuItem, MenuItemConstructorOptions } from 'electron';
|
||||
import { z } from 'zod';
|
||||
|
||||
|
||||
export interface ChromiumHTMLVideoElement extends HTMLVideoElement {
|
||||
@ -23,8 +24,9 @@ export interface ApparentSegmentBase extends SegmentColorIndex {
|
||||
end: number,
|
||||
}
|
||||
|
||||
export const segmentTagsSchema = z.record(z.string(), z.string());
|
||||
|
||||
export type SegmentTags = Record<string, unknown>;
|
||||
export type SegmentTags = z.infer<typeof segmentTagsSchema>
|
||||
|
||||
export type EditingSegmentTags = Record<string, SegmentTags>
|
||||
|
||||
|
@ -7996,6 +7996,7 @@ __metadata:
|
||||
vitest: "npm:^1.2.2"
|
||||
winston: "npm:^3.8.1"
|
||||
yargs-parser: "npm:^21.1.1"
|
||||
zod: "npm:^3.22.5"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@ -12397,3 +12398,10 @@ __metadata:
|
||||
checksum: 2cac84540f65c64ccc1683c267edce396b26b1e931aa429660aefac8fbe0188167b7aee815a3c22fa59a28a58d898d1a2b1825048f834d8d629f4c2a5d443801
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.22.5":
|
||||
version: 3.22.5
|
||||
resolution: "zod@npm:3.22.5"
|
||||
checksum: a60c1b55c4cc824a5d0432ee29d93b087b5d8a1bd2d0f4cd6e7ffe5b602da9cab2f2c27b1ae6c96d88d9f778cc933cead70e08b7944a98893576c61dca5e0c74
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
Loading…
Reference in New Issue
Block a user