fix: clip queue resolves row index by filename, not stored _idx
clipQueue entries snapshotted fileIdx (= _idx) when the queue was armed, but _idx is reassigned positionally on every load(), and pollStatus() reloads the list whenever a recording starts, stops, or rolls over to a new split file. With the newest-first sort a rollover shifts every index by one, so an armed queue then pre-filled the wrong row's cut fields and "Open in file" (O) opened the wrong row while streaming the clip's file into it — during exactly the workflow of reviewing yesterday while today's recording runs. Store only the filename in queue entries and resolve the row index at use time (fileIdxByName); if the file left the listing, Open in file does nothing instead of acting on a wrong row. Also drop seekToSection's sectionIdx parameter: the only remaining caller passed null, leaving the announce branch dead since chips moved to clip playback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+14
-10
@@ -235,6 +235,12 @@ function setCutFields(idx, startSec, endSec) {
|
||||
if (endEl && endSec != null) endEl.value = fmtDur(endSec);
|
||||
}
|
||||
|
||||
// Row index for a filename, resolved at use time: _idx values are reassigned
|
||||
// positionally on every load(), and pollStatus() reloads the list whenever a
|
||||
// recording starts/stops/rolls over — a stored index would point one row off
|
||||
// after that. Null when the file is gone from the current listing.
|
||||
const fileIdxByName = name => allFiles.find(f => f.name === name)?._idx ?? null;
|
||||
|
||||
// idx -> filename, for live-status polling
|
||||
const recMap = new Map();
|
||||
// idx -> [{start,end}], populated after analysis
|
||||
@@ -325,7 +331,7 @@ function parseTime(s) {
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
function seekToSection(idx, filename, startSec, endSec, sectionIdx) {
|
||||
function seekToSection(idx, filename, startSec, endSec) {
|
||||
const pbtn = document.getElementById('pbtn-'+idx);
|
||||
if (pbtn.getAttribute('aria-expanded') !== 'true') togglePlayer(idx, filename);
|
||||
activePlayerIdx = idx;
|
||||
@@ -336,10 +342,6 @@ function seekToSection(idx, filename, startSec, endSec, sectionIdx) {
|
||||
if (audio.readyState >= 1) doSeek();
|
||||
else audio.addEventListener('loadedmetadata', doSeek, {once: true});
|
||||
setCutFields(idx, startSec, endSec);
|
||||
if (sectionIdx != null) {
|
||||
const total = (sectionMap.get(idx) || []).length;
|
||||
announce(`Section ${sectionIdx + 1} of ${total}: ${fmtDur(startSec)} to ${fmtDur(endSec)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- clip player -----------------------------------------------------------
|
||||
@@ -390,7 +392,8 @@ function playClip(i, byScore) {
|
||||
label.title = `${c.filename} @ ${fmtDur(c.start)}–${fmtDur(c.end)}`;
|
||||
document.getElementById('clip-bar').hidden = false;
|
||||
document.body.classList.add('clip-open');
|
||||
setCutFields(c.fileIdx, c.start, c.end);
|
||||
const rowIdx = fileIdxByName(c.filename);
|
||||
if (rowIdx != null) setCutFields(rowIdx, c.start, c.end);
|
||||
announce(text);
|
||||
}
|
||||
|
||||
@@ -409,7 +412,7 @@ function playFileSection(idx, filename, si) {
|
||||
const secs = sectionMap.get(idx) || [];
|
||||
const f = allFiles.find(f => f._idx === idx);
|
||||
const epoch = f ? fileStartEpoch(f.date) : null;
|
||||
clipQueue = secs.map(s => ({fileIdx: idx, filename, start: s.start, end: s.end, score: s.score,
|
||||
clipQueue = secs.map(s => ({filename, start: s.start, end: s.end, score: s.score,
|
||||
absStart: epoch != null ? epoch + s.start : null}));
|
||||
scoreCursor = -1; // explicit jump: next U/I re-anchors here
|
||||
playClip(si);
|
||||
@@ -468,15 +471,17 @@ document.getElementById('clip-audio').addEventListener('ended', () => {
|
||||
function openClipInFile() {
|
||||
const c = clipQueue[clipCursor];
|
||||
if (!c) return;
|
||||
const idx = fileIdxByName(c.filename);
|
||||
if (idx == null) return;
|
||||
document.getElementById('clip-audio').pause();
|
||||
const row = document.getElementById('row-' + c.fileIdx);
|
||||
const row = document.getElementById('row-' + idx);
|
||||
if (row) {
|
||||
const tbl = row.closest('table');
|
||||
if (tbl && tbl.hidden) // file's day is collapsed: expand it
|
||||
document.getElementById('daytgl-' + tbl.id.slice('daytbl-'.length))?.click();
|
||||
row.scrollIntoView({block: 'center'});
|
||||
}
|
||||
seekToSection(c.fileIdx, c.filename, c.start, c.end, null);
|
||||
seekToSection(idx, c.filename, c.start, c.end);
|
||||
}
|
||||
document.getElementById('clip-context').addEventListener('click', openClipInFile);
|
||||
|
||||
@@ -1024,7 +1029,6 @@ async function dayHighlights(dayId, analyzableFiles) {
|
||||
positioned.forEach(({ f, data, fileStart }) => {
|
||||
(data.sections || []).forEach(s => {
|
||||
dayActiveSections.push({
|
||||
fileIdx: f._idx,
|
||||
filename: f.name,
|
||||
start: s.start,
|
||||
end: s.end,
|
||||
|
||||
Reference in New Issue
Block a user