mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-27 16:49:51 -07:00
feat(shortcuts): make clipboard-video-append shortcut configurable (#158)
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
type: changed
|
||||
area: shortcuts
|
||||
|
||||
- Made the clipboard-video playlist shortcut configurable through `shortcuts.appendClipboardVideoToQueue`.
|
||||
@@ -209,7 +209,8 @@
|
||||
"openControllerSelect": "Alt+C", // Accelerator that opens the controller selection and learn-mode modal.
|
||||
"openControllerDebug": "Alt+Shift+C", // Accelerator that opens the controller debug modal with live axis/button readouts.
|
||||
"toggleSubtitleSidebar": "Backslash", // Accelerator that toggles the subtitle sidebar visibility.
|
||||
"toggleNotificationHistory": "CommandOrControl+N" // Accelerator that toggles the overlay notification history panel.
|
||||
"toggleNotificationHistory": "CommandOrControl+N", // Accelerator that toggles the overlay notification history panel.
|
||||
"appendClipboardVideoToQueue": "CommandOrControl+A" // Accelerator that appends a video path from the clipboard to the mpv playlist.
|
||||
}, // Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||
|
||||
// ==========================================
|
||||
|
||||
@@ -655,6 +655,7 @@ See `config.example.jsonc` for detailed configuration options.
|
||||
"openJimaku": "Ctrl+Shift+J",
|
||||
"toggleSubtitleSidebar": "Backslash",
|
||||
"toggleNotificationHistory": "CommandOrControl+N",
|
||||
"appendClipboardVideoToQueue": "CommandOrControl+A",
|
||||
"multiCopyTimeoutMs": 3000
|
||||
}
|
||||
}
|
||||
@@ -681,6 +682,7 @@ See `config.example.jsonc` for detailed configuration options.
|
||||
| `openJimaku` | string \| `null` | Opens the Jimaku search modal (default: `"Ctrl+Shift+J"`) |
|
||||
| `toggleSubtitleSidebar` | string \| `null` | Dispatches the subtitle sidebar toggle action (default: `"Backslash"`). `subtitleSidebar.toggleKey` remains the primary bare-key setting. |
|
||||
| `toggleNotificationHistory` | string \| `null` | Toggles the overlay notification history panel (default: `"CommandOrControl+N"`). The panel slides in from the same edge as notifications (right when notifications are centered). |
|
||||
| `appendClipboardVideoToQueue` | string \| `null` | Appends a video file path from the clipboard to the mpv playlist (default: `"CommandOrControl+A"`). Works whether the overlay or mpv has focus. |
|
||||
|
||||
**See `config.example.jsonc`** for the complete list of shortcut configuration options.
|
||||
|
||||
@@ -820,7 +822,7 @@ When automatic card updates are disabled, new cards are detected but not automat
|
||||
| `Ctrl+Shift+A` | Mark the last added Anki card as an audio card (sets IsAudioCard, SentenceAudio, Sentence, Picture) |
|
||||
| `Ctrl+D` | Open loaded character dictionary manager |
|
||||
| `Ctrl+Shift+O` | Open runtime options palette (session-only live toggles) |
|
||||
| `Ctrl/Cmd+A` | Append clipboard video path to MPV playlist (fixed, not currently configurable) |
|
||||
| `Ctrl/Cmd+A` | Append clipboard video path to MPV playlist (configurable via `shortcuts.appendClipboardVideoToQueue`) |
|
||||
|
||||
**Multi-line copy workflow:**
|
||||
|
||||
|
||||
@@ -209,7 +209,8 @@
|
||||
"openControllerSelect": "Alt+C", // Accelerator that opens the controller selection and learn-mode modal.
|
||||
"openControllerDebug": "Alt+Shift+C", // Accelerator that opens the controller debug modal with live axis/button readouts.
|
||||
"toggleSubtitleSidebar": "Backslash", // Accelerator that toggles the subtitle sidebar visibility.
|
||||
"toggleNotificationHistory": "CommandOrControl+N" // Accelerator that toggles the overlay notification history panel.
|
||||
"toggleNotificationHistory": "CommandOrControl+N", // Accelerator that toggles the overlay notification history panel.
|
||||
"appendClipboardVideoToQueue": "CommandOrControl+A" // Accelerator that appends a video path from the clipboard to the mpv playlist.
|
||||
}, // Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
||||
|
||||
// ==========================================
|
||||
|
||||
@@ -66,9 +66,8 @@ These control playback and subtitle display. They require overlay window focus.
|
||||
| `Ctrl+W` | Quit mpv |
|
||||
| `Right-click` | Toggle pause (outside subtitle area) |
|
||||
| `Right-click + drag` | Reposition subtitles (on subtitle area) |
|
||||
| `Ctrl/Cmd+A` | Append clipboard video path to mpv playlist |
|
||||
|
||||
The mpv-command rows above (`Space`, `F`, `J`, `Shift+J`, the seek/sub-seek/sub-step/sub-delay keys, replay/play-next, and quit) are merged from the `keybindings` config array and can be remapped or disabled there. `V`, `Ctrl/Cmd+A`, and the mouse actions are built-in overlay behaviors and are not part of the `keybindings` array. The playlist browser opens a split overlay modal with sibling video files on the left and the live mpv playlist on the right.
|
||||
The mpv-command rows above (`Space`, `F`, `J`, `Shift+J`, the seek/sub-seek/sub-step/sub-delay keys, replay/play-next, and quit) are merged from the `keybindings` config array and can be remapped or disabled there. `V` and the mouse actions are built-in overlay behaviors and are not part of the `keybindings` array. The playlist browser opens a split overlay modal with sibling video files on the left and the live mpv playlist on the right.
|
||||
|
||||
On macOS managed playback, SubMiner disables mpv's menu-bar shortcuts so configured SubMiner shortcuts like `Cmd+Shift+O` reach the mpv plugin instead of opening native mpv menu actions.
|
||||
|
||||
@@ -86,6 +85,7 @@ Mouse-hover playback behavior is configured separately from shortcuts: `subtitle
|
||||
| `Ctrl/Cmd+N` | Toggle overlay notification history panel | `shortcuts.toggleNotificationHistory` |
|
||||
| `Ctrl+Alt+C` | Open the manual YouTube subtitle picker | `keybindings` |
|
||||
| `Ctrl+Alt+S` | Open subtitle sync (subsync) modal | `shortcuts.triggerSubsync` |
|
||||
| `Ctrl/Cmd+A` | Append clipboard video path to mpv playlist | `shortcuts.appendClipboardVideoToQueue` |
|
||||
| `\` | Toggle subtitle sidebar | `subtitleSidebar.toggleKey` (overlay) / `shortcuts.toggleSubtitleSidebar` (mpv session binding) |
|
||||
| `` ` `` | Toggle stats overlay | `stats.toggleKey` |
|
||||
| `W` | Mark current video watched and advance to next in queue | `stats.markWatchedKey` |
|
||||
|
||||
@@ -103,6 +103,7 @@ export const CORE_DEFAULT_CONFIG: Pick<
|
||||
openControllerDebug: 'Alt+Shift+C',
|
||||
toggleSubtitleSidebar: 'Backslash',
|
||||
toggleNotificationHistory: 'CommandOrControl+N',
|
||||
appendClipboardVideoToQueue: 'CommandOrControl+A',
|
||||
},
|
||||
secondarySub: {
|
||||
secondarySubLanguages: [],
|
||||
|
||||
@@ -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.',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
},
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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' },
|
||||
],
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -33,6 +33,7 @@ const SESSION_ACTION_IDS: SessionActionId[] = [
|
||||
'markAudioCard',
|
||||
'toggleSubtitleSidebar',
|
||||
'toggleNotificationHistory',
|
||||
'appendClipboardVideoToQueue',
|
||||
'openRuntimeOptions',
|
||||
'openSessionHelp',
|
||||
'openCharacterDictionaryManager',
|
||||
|
||||
@@ -127,6 +127,7 @@ export interface ShortcutsConfig {
|
||||
openControllerDebug?: string | null;
|
||||
toggleSubtitleSidebar?: string | null;
|
||||
toggleNotificationHistory?: string | null;
|
||||
appendClipboardVideoToQueue?: string | null;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
|
||||
@@ -14,6 +14,7 @@ export type SessionActionId =
|
||||
| 'toggleSecondarySub'
|
||||
| 'toggleSubtitleSidebar'
|
||||
| 'toggleNotificationHistory'
|
||||
| 'appendClipboardVideoToQueue'
|
||||
| 'markAudioCard'
|
||||
| 'openRuntimeOptions'
|
||||
| 'openSessionHelp'
|
||||
|
||||
Reference in New Issue
Block a user