feat(overlay): run session help commands with Enter or double-click (#275)

This commit is contained in:
2026-09-29 18:48:56 -07:00
committed by GitHub
parent 4e43c5c6e1
commit 0bbd8617f4
16 changed files with 701 additions and 49 deletions
@@ -0,0 +1,58 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { dispatchConfiguredMpvCommand } from './mpv-command-dispatch';
async function dispatch(command: (string | number)[], paused: boolean | null | Error) {
const sent: (string | number)[][] = [];
dispatchConfiguredMpvCommand(command, {
getPlaybackPaused: async () => {
if (paused instanceof Error) throw paused;
return paused;
},
sendMpvCommand: (mpvCommand) => sent.push(mpvCommand),
});
await new Promise((resolve) => setTimeout(resolve, 0));
return sent;
}
test('subtitle seeks keep paused or unknown playback paused', async () => {
const repaused = [
['sub-seek', 1],
['set_property', 'pause', 'yes'],
];
assert.deepEqual(await dispatch(['sub-seek', 1], true), repaused);
assert.deepEqual(await dispatch(['sub-seek', 1], null), repaused);
});
test('subtitle seeks leave running playback alone', async () => {
assert.deepEqual(await dispatch(['sub-seek', -1], false), [['sub-seek', -1]]);
assert.deepEqual(await dispatch(['sub-seek', -1], new Error('ipc down')), [['sub-seek', -1]]);
});
test('other mpv commands are sent as-is', async () => {
assert.deepEqual(await dispatch(['cycle', 'pause'], true), [['cycle', 'pause']]);
});
test('a failed re-pause does not resend the subtitle seek', async () => {
const sent: (string | number)[][] = [];
const originalConsoleError = console.error;
console.error = () => {};
try {
dispatchConfiguredMpvCommand(['sub-seek', 1], {
getPlaybackPaused: async () => true,
sendMpvCommand: (command) => {
sent.push(command);
if (command[0] === 'set_property') throw new Error('ipc closed');
},
});
await new Promise((resolve) => setTimeout(resolve, 0));
} finally {
console.error = originalConsoleError;
}
assert.deepEqual(sent, [
['sub-seek', 1],
['set_property', 'pause', 'yes'],
]);
});
@@ -0,0 +1,38 @@
type MpvCommand = (string | number)[];
function isSubtitleSeekCommand(command: MpvCommand): command is [string, number] {
return command[0] === 'sub-seek' && typeof command[1] === 'number';
}
/**
* Sends a configured mpv command. Subtitle seeks re-pause afterwards unless playback
* is known to be running, so stepping lines from a paused video stays paused.
*/
export function dispatchConfiguredMpvCommand(
command: MpvCommand,
deps: {
getPlaybackPaused: () => Promise<boolean | null>;
sendMpvCommand: (command: MpvCommand) => void;
},
): void {
if (!isSubtitleSeekCommand(command)) {
deps.sendMpvCommand(command);
return;
}
// The fallback only covers a failed pause lookup, so a failed re-pause never resends the seek.
void deps
.getPlaybackPaused()
.then(
(paused) => {
deps.sendMpvCommand(command);
if (paused !== false) {
deps.sendMpvCommand(['set_property', 'pause', 'yes']);
}
},
() => {
deps.sendMpvCommand(command);
},
)
.catch((error: unknown) => console.error('Could not send mpv command', error));
}