Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import {
Alert,
Box,
Button,
Chip,
Collapse,
FormControl,
Expand All @@ -40,11 +41,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
import type {
BrowserUploadFile,
WebUploadFramePlan,
WebUploadPlan,
} from "./web-upload-types";
import type { BrowserUploadFile, WebUploadFramePlan, WebUploadPlan } from "./web-upload-types";
import {
webUploadColors,
webUploadMotion,
Expand Down Expand Up @@ -82,6 +79,7 @@ interface PairingPreviewPanelProps {
hasBlockingIssues: boolean;
onExpandedFrameChange: (frameId: string | null) => void;
onHeatmapReferenceChange: (label: string) => void;
onToggleTitleDisplayMode: () => void;
onRenameColumn: (column: UploadPlanImageColumn, nextLabel: string) => void;
onReorder: (activeFrameId: string, overFrameId: string | null) => void;
}
Expand All @@ -97,13 +95,7 @@ function alternateAssetForLabel(frame: WebUploadFramePlan | null, label: string)
return frame?.misc.find((asset) => asset.label === label) ?? null;
}

function SmallLazyThumbnail({
alt,
source,
}: {
alt: string;
source: BrowserUploadFile | null;
}) {
function SmallLazyThumbnail({ alt, source }: { alt: string; source: BrowserUploadFile | null }) {
const rootRef = useRef<HTMLSpanElement | null>(null);
const [isVisible, setIsVisible] = useState(false);
const [url, setUrl] = useState<string | null>(null);
Expand Down Expand Up @@ -213,21 +205,27 @@ function ImageCell({ muted = false, path, source }: ImageCellProps) {

function IssueStatus({ row }: { row: FramePreviewRow }) {
if (row.hasError) {
return <Typography aria-label="错误" sx={{ userSelect: "none" }}>⛔</Typography>;
return (
<Typography aria-label="错误" sx={{ userSelect: "none" }}>
</Typography>
);
}
if (row.hasWarning) {
return <Typography aria-label="警告" sx={{ userSelect: "none" }}>⚠️</Typography>;
return (
<Typography aria-label="警告" sx={{ userSelect: "none" }}>
⚠️
</Typography>
);
}
return <Typography aria-label="可用" sx={{ userSelect: "none" }}>✅</Typography>;
return (
<Typography aria-label="可用" sx={{ userSelect: "none" }}>
</Typography>
);
}

function ExpandedPreview({
frame,
urls,
}: {
frame: WebUploadFramePlan;
urls: PreviewUrls | null;
}) {
function ExpandedPreview({ frame, urls }: { frame: WebUploadFramePlan; urls: PreviewUrls | null }) {
return (
<Collapse in={Boolean(urls)} timeout={180} unmountOnExit>
<Box
Expand Down Expand Up @@ -508,10 +506,7 @@ function SortablePairingRow({
const alternate = row.alternateAfter.find((item) => item.label === label);
const alternateAsset = alternateAssetForLabel(previewFrame, label);
return (
<Box
key={label}
sx={{ display: { xs: "none", md: "block" }, minWidth: 0 }}
>
<Box key={label} sx={{ display: { xs: "none", md: "block" }, minWidth: 0 }}>
<ImageCell
muted={!alternate}
path={alternate?.path ?? null}
Expand All @@ -536,10 +531,7 @@ function SortablePairingRow({
/>
</Box>
{previewFrame ? (
<ExpandedPreview
frame={previewFrame}
urls={expanded ? previewUrls : null}
/>
<ExpandedPreview frame={previewFrame} urls={expanded ? previewUrls : null} />
) : null}
</Box>
);
Expand All @@ -553,6 +545,7 @@ export function PairingPreviewPanel({
hasBlockingIssues,
onExpandedFrameChange,
onHeatmapReferenceChange,
onToggleTitleDisplayMode,
onRenameColumn,
onReorder,
}: PairingPreviewPanelProps) {
Expand Down Expand Up @@ -663,6 +656,21 @@ export function PairingPreviewPanel({
</Select>
</FormControl>
) : null}
<Button
size="small"
variant="outlined"
onClick={onToggleTitleDisplayMode}
disabled={!canReorder}
sx={{
height: webUploadSizes.compactControlHeight,
borderRadius: webUploadRadii.control,
px: 1.25,
fontSize: 12,
whiteSpace: "nowrap",
}}
>
{planView.titleDisplayMode === "full" ? "恢复自动帧号" : "使用完整 m2ts-帧号"}
</Button>
<Chip
icon={hasBlockingIssues ? <WarningAmber /> : <CheckCircle />}
label={`${planView.healthyPairCount} / ${planView.frames.length}`}
Expand Down Expand Up @@ -725,7 +733,9 @@ export function PairingPreviewPanel({
<EditableColumnHeader
canEdit={canReorder}
label={label}
onRename={(_label, nextLabel) => onRenameColumn({ kind: "misc", label }, nextLabel)}
onRename={(_label, nextLabel) =>
onRenameColumn({ kind: "misc", label }, nextLabel)
}
/>
</Box>
))}
Expand Down Expand Up @@ -758,7 +768,10 @@ export function PairingPreviewPanel({
</Box>

{planView?.issues.length ? (
<Stack spacing={0.7} sx={{ px: 1.5, py: 1.25, borderTop: "1px solid", borderColor: "divider" }}>
<Stack
spacing={0.7}
sx={{ px: 1.5, py: 1.25, borderTop: "1px solid", borderColor: "divider" }}
>
{planView.issues.slice(0, 3).map((issue, index) => (
<Alert key={`${issue.path}-${index}`} severity={issue.severity} sx={{ py: 0.45 }}>
{issue.message}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
buildPlanView,
compactUploadFilename,
frameIdForFrame,
getNextUploadPlanTitleDisplayMode,
getUploadPlanHeatmapReferenceOptions,
renameUploadPlanAssetLabel,
reorderUploadPlan,
Expand Down Expand Up @@ -72,11 +73,7 @@ describe("web upload view model", () => {

const reordered = reorderUploadPlan(originalPlan, activeId, overId);
expect(reordered).not.toBeNull();
expect(reordered?.frames.map((item) => item.title)).toEqual([
"Frame 3",
"Frame 1",
"Frame 2",
]);
expect(reordered?.frames.map((item) => item.title)).toEqual(["Frame 3", "Frame 1", "Frame 2"]);
expect(buildPlanView(reordered!).frames.map((item) => item.title)).toEqual([
"Frame 3",
"Frame 1",
Expand All @@ -98,9 +95,39 @@ describe("web upload view model", () => {
it("ignores unknown drag ids", () => {
const originalPlan = plan();

expect(reorderUploadPlan(originalPlan, "missing", frameIdForFrame(originalPlan.frames[0]))).toBeNull();
expect(reorderUploadPlan(originalPlan, frameIdForFrame(originalPlan.frames[0]), "missing")).toBeNull();
expect(reorderUploadPlan(originalPlan, frameIdForFrame(originalPlan.frames[0]), null)).toBeNull();
expect(
reorderUploadPlan(originalPlan, "missing", frameIdForFrame(originalPlan.frames[0])),
).toBeNull();
expect(
reorderUploadPlan(originalPlan, frameIdForFrame(originalPlan.frames[0]), "missing"),
).toBeNull();
expect(
reorderUploadPlan(originalPlan, frameIdForFrame(originalPlan.frames[0]), null),
).toBeNull();
});

it("switches to full frame titles and restores the original automatic titles", () => {
const originalPlan = plan();
originalPlan.frames[0] = {
...originalPlan.frames[0],
title: "12-27240",
caption: "WatanaRe Anime Vol1 / 24 fps / frame 27240",
};

const autoView = buildPlanView(originalPlan, "auto");
const fullMode = getNextUploadPlanTitleDisplayMode(autoView.titleDisplayMode);
const fullView = buildPlanView(originalPlan, fullMode);
const restoredMode = getNextUploadPlanTitleDisplayMode(fullView.titleDisplayMode);
const restoredView = buildPlanView(originalPlan, restoredMode);

expect(autoView.frames[0]).toMatchObject({
title: "12-27240",
autoTitle: "12-27240",
fullTitle: "WatanaRe Anime Vol1-27240",
});
expect(fullView.frames[0].title).toBe("WatanaRe Anime Vol1-27240");
expect(originalPlan.frames[0].title).toBe("12-27240");
expect(restoredView.frames[0].title).toBe("12-27240");
});

it("exposes up to three alternate after columns for preview", () => {
Expand Down Expand Up @@ -158,11 +185,7 @@ describe("web upload view model", () => {

const renamed = renameUploadPlanAssetLabel(originalPlan, { kind: "after" }, "Output");

expect(renamed?.frames.map((item) => item.after.label)).toEqual([
"Output",
"Output",
"Output",
]);
expect(renamed?.frames.map((item) => item.after.label)).toEqual(["Output", "Output", "Output"]);
expect(renamed?.heatmapReferenceLabel).toBe("Output");
expect(getUploadPlanHeatmapReferenceOptions(renamed!)).toEqual(["Output"]);
});
Expand All @@ -180,8 +203,12 @@ describe("web upload view model", () => {
expect(renameUploadPlanAssetLabel(originalPlan, { kind: "before" }, "")).toBeNull();
expect(renameUploadPlanAssetLabel(originalPlan, { kind: "before" }, "After")).toBeNull();
expect(renameUploadPlanAssetLabel(originalPlan, { kind: "after" }, "Rip")).toBeNull();
expect(renameUploadPlanAssetLabel(originalPlan, { kind: "misc", label: "Rip" }, "Degrain")).toBeNull();
expect(renameUploadPlanAssetLabel(originalPlan, { kind: "misc", label: "Rip" }, "Heatmap")).toBeNull();
expect(
renameUploadPlanAssetLabel(originalPlan, { kind: "misc", label: "Rip" }, "Degrain"),
).toBeNull();
expect(
renameUploadPlanAssetLabel(originalPlan, { kind: "misc", label: "Rip" }, "Heatmap"),
).toBeNull();
});

it("only exposes global heatmap references available on every frame", () => {
Expand Down
Loading