Files
siftlode/frontend/src/lib/timecode.ts
T
peter c489b60c8b fix(clip): clip mode keeps its range, its focus and its keys through the player's own moves
- A mark on the very end seeks a moment short of it; while a clip is marked the end of the item
  neither closes the player nor steps the queue, and Plex's auto-skip waits.
- Plex: Escape and Back peel what is over the picture (info, up-next, the countdown) before the
  clip; Back under a dialog is the dialog's; no range opens while the length is unknown.
- YouTube: the focus goes back to the scissors the user can see, else the player; the panel keeps
  its name, Loop and focus when the view is maximised or restored under it.
- A typed time stays in its field until taken, then shows where it landed; unreadable stays marked
  invalid; a decimal comma is read (ui/TimecodeField, the editor's fields too).
- RangeTrack: a drag is its pointer's, a cancelled drag reports final, an adornment sits beside the
  slider, a caller-chosen focus ring (amber on the clip strip). CropFrame: a drag is its pointer's.
- The editor's remove-cut is a real button at the cut's foot; a segment's keep toggle sits over the
  cut's hit area.
2026-10-10 15:35:21 +02:00

32 lines
1.4 KiB
TypeScript

// An editing timecode: to the tenth of a second, the precision a cut is marked at (the editor's
// segment fields, the clip panel's In / Out). Hours appear only when the item reaches an hour, so a
// short's "0:12.4" stays short and a film reads "1:02:03.4". `formatDuration` is the whole-second
// clock for a card or a player bar; this one is for a field someone types a cut into.
/** `s` as `m:ss.d`, or `h:mm:ss.d` when `hours` (pass `duration >= 3600` so one item's fields agree). */
export function formatTimecode(s: number, hours = false): string {
if (!isFinite(s) || s < 0) s = 0;
const tenths = Math.round(s * 10);
const h = Math.floor(tenths / 36000);
const m = Math.floor((tenths % 36000) / 600);
const sec = ((tenths % 600) / 10).toFixed(1).padStart(4, "0");
return hours || h > 0 ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`;
}
/** A typed timecode back to seconds: `h:mm:ss.d`, `m:ss.d` or bare seconds, its decimal a point or
* a comma (how a Hungarian hand writes one). Null when it is not one. */
export function parseTimecode(v: string): number | null {
const parts = v
.trim()
.replace(/,(\d*)$/, ".$1")
.split(":");
if (parts.length > 3 || parts.some((p) => p.trim() === "")) return null;
let total = 0;
for (const p of parts) {
const n = Number(p);
if (!isFinite(n) || n < 0) return null;
total = total * 60 + n;
}
return total;
}