feat(subtitles): add local Japanese subtitle generation (#240)

This commit is contained in:
2026-09-15 21:43:11 -07:00
committed by GitHub
parent 6841a37a22
commit 2831e05124
90 changed files with 4934 additions and 57 deletions
@@ -5,6 +5,7 @@ import type { ConfiguredShortcuts } from '../utils/shortcut-config';
import { DEFAULT_CONFIG, DEFAULT_KEYBINDINGS, SPECIAL_COMMANDS } from '../../config/definitions';
import { resolveConfiguredShortcuts } from '../utils/shortcut-config';
import { buildPluginSessionBindingsArtifact, compileSessionBindings } from './session-bindings';
import { parseSessionActionDispatchRequest } from '../../shared/ipc/validators';
function createShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): ConfiguredShortcuts {
return {
@@ -23,6 +24,7 @@ function createShortcuts(overrides: Partial<ConfiguredShortcuts> = {}): Configur
openRuntimeOptions: null,
openJimaku: null,
openTsukihime: null,
openSubtitleGeneration: null,
openSessionHelp: null,
openControllerSelect: null,
openControllerDebug: null,
@@ -37,6 +39,50 @@ function createKeybinding(key: string, command: Keybinding['command']): Keybindi
return { key, command };
}
test('subtitle generation shortcut compiles for overlay and mpv without conflicting with field grouping', () => {
for (const platform of ['linux', 'darwin', 'win32'] as const) {
const result = compileSessionBindings({
shortcuts: resolveConfiguredShortcuts(DEFAULT_CONFIG, DEFAULT_CONFIG),
keybindings: DEFAULT_KEYBINDINGS,
platform,
});
const binding = result.bindings.find(
(entry) =>
entry.actionType === 'session-action' && entry.actionId === 'openSubtitleGeneration',
);
assert.ok(binding);
assert.deepEqual(binding.key, { code: 'KeyG', modifiers: ['ctrl', 'shift'] });
assert.equal(
result.warnings.some(
(warning) =>
warning.path === 'shortcuts.openSubtitleGeneration' ||
warning.conflictingPaths?.includes('shortcuts.openSubtitleGeneration'),
),
false,
);
assert.ok(
result.bindings.some(
(entry) =>
entry.actionType === 'session-action' && entry.actionId === 'triggerFieldGrouping',
),
);
const artifact = buildPluginSessionBindingsArtifact({
bindings: [binding],
warnings: [],
numericSelectionTimeoutMs: 3000,
});
const pluginBinding = artifact.bindings[0];
assert.ok(pluginBinding?.actionType === 'session-action');
assert.deepEqual(pluginBinding.cliArgs, [
'--session-action',
'{"actionId":"openSubtitleGeneration"}',
]);
assert.deepEqual(parseSessionActionDispatchRequest({ actionId: 'openSubtitleGeneration' }), {
actionId: 'openSubtitleGeneration',
});
}
});
test('compileSessionBindings merges shortcuts and keybindings into one canonical list', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts({