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
17 changes: 10 additions & 7 deletions src/components/TrimControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { EditRecipe } from "@/lib/types";
import { useState, useEffect, useRef, useCallback } from "react";
import { AlertCircle } from "lucide-react";
import { formatDuration } from "@/lib/utils";
import { formatTrimTime } from "@/lib/utils";
import { useAudioWaveform } from "@/hooks/useAudioWaveform";
import WaveformCanvas from "@/components/WaveformCanvas";

Expand Down Expand Up @@ -34,6 +34,9 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)

const clipLength =
(recipe.trimEnd ?? duration) - recipe.trimStart;
const formattedStart = formatTrimTime(recipe.trimStart);
const formattedEnd = formatTrimTime(recipe.trimEnd ?? duration);
const formattedDuration = formatTrimTime(duration);

const trackRef = useRef<HTMLDivElement>(null);
const dragging = useRef<"start" | "end" | null>(null);
Expand Down Expand Up @@ -116,7 +119,7 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
if (duration > 0 && n >= duration) {
setStart(true);
setStartErrorMsg(
`Start time must be less than duration (${duration.toFixed(1)}s).`
`Start time must be less than duration (${formattedDuration}).`
);
return;
}
Expand Down Expand Up @@ -162,7 +165,7 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
if (duration > 0 && n > duration + 0.01) {
setEnd(true);
setEndErrorMsg(
`End time cannot exceed duration (${duration.toFixed(1)}s).`,
`End time cannot exceed duration (${formattedDuration}).`,
);
return;
}
Expand Down Expand Up @@ -241,7 +244,7 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
htmlFor="trim-start"
className="font-heading mb-1.5 block text-[10px] font-semibold uppercase tracking-wider text-[var(--muted)]"
>
Start (sec)
Start ({formattedStart})
</label>

<input
Expand Down Expand Up @@ -278,7 +281,7 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
htmlFor="trim-end"
className="font-heading mb-1.5 block text-[10px] font-semibold uppercase tracking-wider text-[var(--muted)]"
>
End (sec)
End ({formattedEnd})
</label>

<input
Expand Down Expand Up @@ -313,8 +316,8 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)

{duration > 0 && (
<p className="text-sm text-[var(--muted)] font-heading mt-1">
Clip: {formatDuration(clipLength)} of{" "}
{formatDuration(duration)}
Clip: {formatTrimTime(clipLength)} of{" "}
{formattedDuration}
</p>
)}
{recipe.trimEnd !== null &&
Expand Down
24 changes: 22 additions & 2 deletions src/lib/tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { formatBytes, formatDuration } from "../utils";
import { formatBytes, formatDuration, formatTrimTime } from "../utils";

describe("formatBytes", () => {
it("returns '0 Bytes' for zero input", () => {
Expand Down Expand Up @@ -68,4 +68,24 @@ describe("formatDuration", () => {
it("handles negative values", () => {
expect(formatDuration(-1)).toBe("0:00");
});
});
});

describe("formatTrimTime", () => {
it("formats raw seconds as MM:SS.d", () => {
expect(formatTrimTime(65.5)).toBe("01:05.5");
});

it("pads seconds under one minute", () => {
expect(formatTrimTime(5)).toBe("00:05.0");
});

it("preserves one decimal place", () => {
expect(formatTrimTime(12.34)).toBe("00:12.3");
});

it("handles invalid values", () => {
expect(formatTrimTime(NaN)).toBe("00:00.0");
expect(formatTrimTime(Infinity)).toBe("00:00.0");
expect(formatTrimTime(-1)).toBe("00:00.0");
});
});
13 changes: 12 additions & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,15 @@ export function formatDuration(seconds: number): string {
}

return `${minutes}:${secs.toString().padStart(2, "0")}`;
}
}

export function formatTrimTime(seconds: number): string {
if (!Number.isFinite(seconds) || seconds < 0) {
return "00:00.0";
}

const minutes = Math.floor(seconds / 60);
const secs = (seconds % 60).toFixed(1);

return `${String(minutes).padStart(2, "0")}:${secs.padStart(4, "0")}`;
}