feat: Shift+J/K/U/I jump to first/last/loudest/quietest section
Holding Shift with the section-navigation keys jumps straight to the extreme in that direction instead of stepping one at a time: - Shift+J / Shift+K -> first / last section in time order - Shift+U / Shift+I -> loudest / quietest section Works in both clip-queue navigation (stepClip gains a jump flag) and in-player full-file navigation. Visible key-hint note and README updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+22
-9
@@ -410,18 +410,20 @@ const advanceMode = () =>
|
||||
document.querySelector('input[name="clip-adv"]:checked')?.value || 'off';
|
||||
|
||||
// Step the clip queue by dir (±1): in time order, or with byScore down/up
|
||||
// the loudest-first ranking (next = next quieter).
|
||||
function stepClip(dir, byScore) {
|
||||
// the loudest-first ranking (next = next quieter). With jump=true, go straight
|
||||
// to the extreme in that direction (first/last section, or loudest/quietest).
|
||||
function stepClip(dir, byScore, jump) {
|
||||
if (!clipQueue.length) return;
|
||||
if (byScore) {
|
||||
const order = scoreOrder(clipQueue);
|
||||
const pos = order.indexOf(clipCursor); // -1: nothing played yet
|
||||
const next = pos === -1 ? (dir > 0 ? 0 : -1) : pos + dir;
|
||||
const next = jump ? (dir > 0 ? order.length - 1 : 0)
|
||||
: pos === -1 ? (dir > 0 ? 0 : -1) : pos + dir;
|
||||
if (next >= 0 && next < order.length) playClip(order[next]);
|
||||
else announce(dir < 0 ? 'Loudest highlight reached' : 'End of highlights');
|
||||
return;
|
||||
}
|
||||
const i = clipCursor + dir;
|
||||
const i = jump ? (dir > 0 ? clipQueue.length - 1 : 0) : clipCursor + dir;
|
||||
if (i >= 0 && i < clipQueue.length) playClip(i);
|
||||
else announce(dir < 0 ? 'Beginning of sections' : 'End of sections');
|
||||
}
|
||||
@@ -534,8 +536,9 @@ async function analyse(idx, filename, cell, btn, force = false) {
|
||||
}
|
||||
|
||||
// J/K = previous/next section in time order, U/I = up/down the loudest-first
|
||||
// ranking, O = open the current clip in the full file.
|
||||
// Only when focus is not in an input.
|
||||
// ranking, O = open the current clip in the full file. Holding Shift jumps to
|
||||
// the extreme in that direction: Shift+J/K = first/last section in time,
|
||||
// Shift+U/I = loudest/quietest. Only when focus is not in an input.
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
||||
@@ -545,10 +548,11 @@ document.addEventListener('keydown', e => {
|
||||
if (key === 'o') { openClipInFile(); return; }
|
||||
const dir = (key === 'j' || key === 'u') ? -1 : 1;
|
||||
const byScore = key === 'u' || key === 'i';
|
||||
const jump = e.shiftKey;
|
||||
|
||||
// Clip queue navigation (a chip was clicked or day highlights are loaded)
|
||||
if (clipQueue.length) {
|
||||
stepClip(dir, byScore);
|
||||
stepClip(dir, byScore, jump);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -570,10 +574,12 @@ document.addEventListener('keydown', e => {
|
||||
if (byScore) {
|
||||
// U/I walk the ranking; the anchor is the section the playhead sits in
|
||||
// (incl. its preroll lead-in). Anywhere else, I starts at the loudest.
|
||||
// Shift jumps straight to the loudest (Shift+U) or quietest (Shift+I).
|
||||
const order = scoreOrder(all);
|
||||
const curIdx = all.findIndex(s => cur >= s.start - preroll - 0.5 && cur <= s.end + 0.5);
|
||||
const pos = curIdx === -1 ? -1 : order.indexOf(curIdx);
|
||||
const next = pos === -1 ? (dir > 0 ? 0 : -1) : pos + dir;
|
||||
const next = jump ? (dir > 0 ? order.length - 1 : 0)
|
||||
: pos === -1 ? (dir > 0 ? 0 : -1) : pos + dir;
|
||||
if (next >= 0 && next < order.length)
|
||||
jumpTo(all[order[next]], next, order.length, 'Highlight');
|
||||
else
|
||||
@@ -581,6 +587,13 @@ document.addEventListener('keydown', e => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shift+J/K jump to the first/last section regardless of the playhead.
|
||||
if (jump) {
|
||||
const i = dir < 0 ? 0 : all.length - 1;
|
||||
jumpTo(all[i], i, all.length, 'Section');
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir < 0) {
|
||||
for (let i = all.length - 1; i >= 0; i--) {
|
||||
if (all[i].start < cur - 1) { jumpTo(all[i], i, all.length, 'Section'); return; }
|
||||
@@ -1012,7 +1025,7 @@ async function dayHighlights(dayId, analyzableFiles) {
|
||||
const note = document.createElement('p');
|
||||
note.className = 'quiet';
|
||||
note.style.marginTop = '6px';
|
||||
note.textContent = 'J / K plays through all sections in time order, U / I by loudness (loudest first)';
|
||||
note.textContent = 'J / K plays through all sections in time order, U / I by loudness (loudest first). Hold Shift to jump to the first / last section (Shift + J / K) or the loudest / quietest (Shift + U / I)';
|
||||
box.appendChild(note);
|
||||
|
||||
const MAX_DAY_CHIPS = 50;
|
||||
|
||||
Reference in New Issue
Block a user