feat(shortcuts): make clipboard-video-append shortcut configurable (#158)

This commit is contained in:
2026-07-11 23:36:44 -07:00
committed by GitHub
parent 8797719a09
commit 6ab3d823a4
26 changed files with 87 additions and 18 deletions
+1
View File
@@ -103,6 +103,7 @@ export const CORE_DEFAULT_CONFIG: Pick<
openControllerDebug: 'Alt+Shift+C',
toggleSubtitleSidebar: 'Backslash',
toggleNotificationHistory: 'CommandOrControl+N',
appendClipboardVideoToQueue: 'CommandOrControl+A',
},
secondarySub: {
secondarySubLanguages: [],
+6
View File
@@ -646,5 +646,11 @@ export function buildCoreConfigOptionRegistry(
defaultValue: defaultConfig.shortcuts.toggleNotificationHistory,
description: 'Accelerator that toggles the overlay notification history panel.',
},
{
path: 'shortcuts.appendClipboardVideoToQueue',
kind: 'string',
defaultValue: defaultConfig.shortcuts.appendClipboardVideoToQueue,
description: 'Accelerator that appends a video path from the clipboard to the mpv playlist.',
},
];
}
+5
View File
@@ -236,7 +236,12 @@ export function applyCoreDomainConfig(context: ResolveContext): void {
'openCharacterDictionaryManager',
'openRuntimeOptions',
'openJimaku',
'openSessionHelp',
'openControllerSelect',
'openControllerDebug',
'toggleSubtitleSidebar',
'toggleNotificationHistory',
'appendClipboardVideoToQueue',
] as const;
for (const key of shortcutKeys) {
+1 -1
View File
@@ -600,7 +600,7 @@ function subsectionForPath(path: string): string | undefined {
) {
return 'Open Panels';
}
if (leaf === 'triggerSubsync') return 'Playback';
if (leaf === 'triggerSubsync' || leaf === 'appendClipboardVideoToQueue') return 'Playback';
return undefined;
}
return undefined;
@@ -33,6 +33,7 @@ function makeShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): Configured
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
...overrides,
};
}
@@ -28,6 +28,7 @@ function createShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): Configur
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
...overrides,
};
}
@@ -26,6 +26,7 @@ function createDeps(overrides: Partial<SessionActionExecutorDeps> = {}) {
toggleSecondarySub: () => calls.push('secondary'),
toggleSubtitleSidebar: () => calls.push('sidebar'),
toggleNotificationHistory: () => calls.push('notification-history'),
appendClipboardVideoToQueue: () => calls.push('append-clipboard-video'),
markLastCardAsAudioCard: async () => {
calls.push('audio');
},
+4
View File
@@ -15,6 +15,7 @@ export interface SessionActionExecutorDeps {
toggleSecondarySub: () => void;
toggleSubtitleSidebar: () => void;
toggleNotificationHistory: () => void;
appendClipboardVideoToQueue: () => void;
markLastCardAsAudioCard: () => Promise<void>;
markActiveVideoWatched: () => Promise<boolean>;
openRuntimeOptionsPalette: () => void;
@@ -82,6 +83,9 @@ export async function dispatchSessionAction(
case 'toggleNotificationHistory':
deps.toggleNotificationHistory();
return;
case 'appendClipboardVideoToQueue':
deps.appendClipboardVideoToQueue();
return;
case 'markAudioCard':
await deps.markLastCardAsAudioCard();
return;
@@ -27,6 +27,7 @@ function createShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): Configur
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
...overrides,
};
}
@@ -515,6 +516,8 @@ test('compileSessionBindings wires every configured shortcut key into the shared
'openControllerSelect',
'openControllerDebug',
'toggleSubtitleSidebar',
'toggleNotificationHistory',
'appendClipboardVideoToQueue',
];
const shortcuts = createShortcuts();
shortcutKeys.forEach((key, index) => {
@@ -582,6 +585,42 @@ test('buildPluginSessionBindingsArtifact emits CLI args for plugin-bound session
});
});
test('appendClipboardVideoToQueue shortcut compiles to a session action with plugin CLI args', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts({
appendClipboardVideoToQueue: 'CommandOrControl+A',
}),
keybindings: [],
platform: 'linux',
});
assert.deepEqual(result.warnings, []);
const binding = result.bindings.find(
(candidate) =>
candidate.actionType === 'session-action' &&
candidate.actionId === 'appendClipboardVideoToQueue',
);
assert.ok(binding);
assert.deepEqual(binding.key, { code: 'KeyA', modifiers: ['ctrl'] });
const artifact = buildPluginSessionBindingsArtifact({
bindings: result.bindings,
warnings: result.warnings,
numericSelectionTimeoutMs: 2500,
now: new Date('2026-05-26T00:00:00.000Z'),
});
const pluginBinding = artifact.bindings.find(
(candidate) =>
candidate.actionType === 'session-action' &&
candidate.actionId === 'appendClipboardVideoToQueue',
);
assert.ok(pluginBinding && 'cliArgs' in pluginBinding);
assert.equal(pluginBinding.cliArgs?.[0], '--session-action');
assert.deepEqual(JSON.parse(pluginBinding.cliArgs?.[1] ?? ''), {
actionId: 'appendClipboardVideoToQueue',
});
});
test('buildPluginSessionBindingsArtifact preserves plugin selector CLI for no-count multi-line actions', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts({
+1
View File
@@ -60,6 +60,7 @@ const SESSION_SHORTCUT_ACTIONS: Array<{
{ key: 'openControllerDebug', actionId: 'openControllerDebug' },
{ key: 'toggleSubtitleSidebar', actionId: 'toggleSubtitleSidebar' },
{ key: 'toggleNotificationHistory', actionId: 'toggleNotificationHistory' },
{ key: 'appendClipboardVideoToQueue', actionId: 'appendClipboardVideoToQueue' },
];
function normalizeModifiers(modifiers: SessionKeyModifier[]): SessionKeyModifier[] {
+2
View File
@@ -20,6 +20,7 @@ export interface ConfiguredShortcuts {
openControllerDebug: string | null | undefined;
toggleSubtitleSidebar: string | null | undefined;
toggleNotificationHistory: string | null | undefined;
appendClipboardVideoToQueue: string | null | undefined;
}
export function resolveConfiguredShortcuts(
@@ -69,5 +70,6 @@ export function resolveConfiguredShortcuts(
openControllerDebug: normalizeShortcut(shortcutValue('openControllerDebug')),
toggleSubtitleSidebar: normalizeShortcut(shortcutValue('toggleSubtitleSidebar')),
toggleNotificationHistory: normalizeShortcut(shortcutValue('toggleNotificationHistory')),
appendClipboardVideoToQueue: normalizeShortcut(shortcutValue('appendClipboardVideoToQueue')),
};
}
+3
View File
@@ -5416,6 +5416,9 @@ async function dispatchSessionAction(request: SessionActionDispatchRequest): Pro
toggleSecondarySub: () => handleCycleSecondarySubMode(),
toggleSubtitleSidebar: () => toggleSubtitleSidebar(),
toggleNotificationHistory: () => toggleNotificationHistoryPanel(),
appendClipboardVideoToQueue: () => {
appendClipboardVideoToQueue();
},
markLastCardAsAudioCard: () => markLastCardAsAudioCard(),
markActiveVideoWatched: async () => {
ensureImmersionTrackerStarted();
@@ -24,6 +24,7 @@ function createShortcuts(): ConfiguredShortcuts {
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
};
}
@@ -28,6 +28,7 @@ function createShortcuts(): ConfiguredShortcuts {
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
};
}
+1 -1
View File
@@ -95,6 +95,7 @@ function createEmptyShortcuts(): ConfiguredShortcuts {
openControllerDebug: null,
toggleSubtitleSidebar: null,
toggleNotificationHistory: null,
appendClipboardVideoToQueue: null,
};
}
@@ -508,7 +509,6 @@ function createKeyboardHandlerHarness() {
openControllerDebugModal: () => {
openControllerDebugCount += 1;
},
appendClipboardVideoToQueue: () => {},
getPlaybackPaused: () => testGlobals.getPlaybackPaused(),
});
-7
View File
@@ -28,7 +28,6 @@ export function createKeyboardHandlers(
}) => void;
openControllerSelectModal?: () => void;
openControllerDebugModal?: () => void;
appendClipboardVideoToQueue: () => void;
getPlaybackPaused: () => Promise<boolean | null>;
toggleSubtitleSidebarModal?: () => void;
},
@@ -1221,12 +1220,6 @@ export function createKeyboardHandlers(
return;
}
if ((e.ctrlKey || e.metaKey) && !e.altKey && !e.shiftKey && e.code === 'KeyA' && !e.repeat) {
e.preventDefault();
options.appendClipboardVideoToQueue();
return;
}
const keyString = keyEventToString(e);
const binding = ctx.state.sessionBindingMap.get(keyString);
if (binding) {
+3 -1
View File
@@ -203,6 +203,8 @@ function describeSessionAction(
return 'Toggle subtitle sidebar';
case 'toggleNotificationHistory':
return 'Toggle notification history';
case 'appendClipboardVideoToQueue':
return 'Append clipboard video path to playlist';
case 'markAudioCard':
return 'Mark audio card';
case 'markWatched':
@@ -267,6 +269,7 @@ function sectionForSessionBinding(binding: CompiledSessionBinding): string {
return 'Modals and tools';
case 'replayCurrentSubtitle':
case 'playNextSubtitle':
case 'appendClipboardVideoToQueue':
return 'Playback and navigation';
case 'cycleRuntimeOption':
return 'Runtime settings';
@@ -346,7 +349,6 @@ function buildFixedOverlaySections(): SessionHelpSection[] {
title: 'Fixed overlay controls',
rows: [
{ shortcut: 'V', action: 'Toggle primary subtitle bar visibility' },
{ shortcut: 'Ctrl/Cmd + A', action: 'Append clipboard video path to playlist' },
{ shortcut: 'Right-click', action: 'Toggle playback outside subtitle area' },
{ shortcut: 'Right-click + drag', action: 'Reposition subtitles on subtitle area' },
],
-3
View File
@@ -215,9 +215,6 @@ const keyboardHandlers = createKeyboardHandlers(ctx, {
window.electronAPI.notifyOverlayModalOpened('controller-debug');
}
},
appendClipboardVideoToQueue: () => {
void window.electronAPI.appendClipboardVideoToQueue();
},
getPlaybackPaused: () => window.electronAPI.getPlaybackPaused(),
toggleSubtitleSidebarModal: () => {
void subtitleSidebarModal.toggleSubtitleSidebarModal();
+1
View File
@@ -33,6 +33,7 @@ const SESSION_ACTION_IDS: SessionActionId[] = [
'markAudioCard',
'toggleSubtitleSidebar',
'toggleNotificationHistory',
'appendClipboardVideoToQueue',
'openRuntimeOptions',
'openSessionHelp',
'openCharacterDictionaryManager',
+1
View File
@@ -127,6 +127,7 @@ export interface ShortcutsConfig {
openControllerDebug?: string | null;
toggleSubtitleSidebar?: string | null;
toggleNotificationHistory?: string | null;
appendClipboardVideoToQueue?: string | null;
}
export interface Config {
+1
View File
@@ -14,6 +14,7 @@ export type SessionActionId =
| 'toggleSecondarySub'
| 'toggleSubtitleSidebar'
| 'toggleNotificationHistory'
| 'appendClipboardVideoToQueue'
| 'markAudioCard'
| 'openRuntimeOptions'
| 'openSessionHelp'