// The file listing and what the UI knows about each file. A leaf apart from // util.js. import {fileStartEpoch, fmtHM} from './util.js'; // Full listing from /api/files. Reassigned on every load; importers see the // new array through the live binding. export let allFiles = []; // _idx is per filename and stable for the whole session: assigned the first // time a name is seen and never reused. DOM ids (row-, pbtn-, aud-, …) are // built from it, so they survive reloads — that is what lets a re-rendered day // keep focus where it was. It used to be the position in the listing, which // shifted on every rollover and left stored indexes pointing one row off. const idxByName = new Map(); const stableIdx = name => { if (!idxByName.has(name)) idxByName.set(name, idxByName.size); return idxByName.get(name); }; export function setFiles(list) { allFiles = list.map(f => ({...f, _idx: stableIdx(f.name)})); } export function removeFile(name) { allFiles = allFiles.filter(f => f.name !== name); } export const fileByName = name => allFiles.find(f => f.name === name) ?? null; export const fileByIdx = idx => allFiles.find(f => f._idx === idx) ?? null; // Queue entries carry the filename, never a row index; resolve at use time. // Null when the file is gone from the current listing. export const fileIdxByName = name => fileByName(name)?._idx ?? null; export const isAnalysable = f => f.ext === 'wav' || f.ext === 'flac'; // idx -> [{start, end, score}], populated by analyse() export const sectionMap = new Map(); // What a recording is called wherever it is spoken: its wall-clock span from // the filename clock, "22:00 to 23:00". The filename itself ("20260523_220000") // reads as a string of numbers. Non-standard names fall back to the name. export function fileSpan(f) { const start = fileStartEpoch(f.date); if (start == null) return f.name; if (f.recording) return `${fmtHM(start)} to now`; if (f.duration == null) return fmtHM(start); return `${fmtHM(start)} to ${fmtHM(Math.round((start + f.duration) / 60) * 60)}`; }