mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
fix: address follow-up review feedback
This commit is contained in:
@@ -321,7 +321,6 @@ test('launcher forwards --args to mpv as parsed tokens', { timeout: 15000 }, ()
|
|||||||
|
|
||||||
fs.mkdirSync(binDir, { recursive: true });
|
fs.mkdirSync(binDir, { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
|
||||||
fs.writeFileSync(videoPath, 'fake video content');
|
fs.writeFileSync(videoPath, 'fake video content');
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
||||||
@@ -408,7 +407,6 @@ test('launcher forwards non-info log level into mpv plugin script opts', { timeo
|
|||||||
|
|
||||||
fs.mkdirSync(binDir, { recursive: true });
|
fs.mkdirSync(binDir, { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
|
||||||
fs.writeFileSync(videoPath, 'fake video content');
|
fs.writeFileSync(videoPath, 'fake video content');
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
||||||
@@ -485,7 +483,6 @@ test('launcher routes youtube urls through regular playback startup', { timeout:
|
|||||||
|
|
||||||
fs.mkdirSync(binDir, { recursive: true });
|
fs.mkdirSync(binDir, { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
||||||
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
path.join(xdgConfigHome, 'SubMiner', 'setup-state.json'),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|||||||
@@ -295,10 +295,11 @@ const texthookerService = new services_1.Texthooker(() => {
|
|||||||
const config = getResolvedConfig();
|
const config = getResolvedConfig();
|
||||||
const characterDictionaryEnabled = config.anilist.characterDictionary.enabled &&
|
const characterDictionaryEnabled = config.anilist.characterDictionary.enabled &&
|
||||||
yomitanProfilePolicy.isCharacterDictionaryEnabled();
|
yomitanProfilePolicy.isCharacterDictionaryEnabled();
|
||||||
const knownAndNPlusOneEnabled = getRuntimeBooleanOption('subtitle.annotation.nPlusOne', config.ankiConnect.nPlusOne.enabled);
|
const knownWordColoringEnabled = getRuntimeBooleanOption('subtitle.annotation.knownWords.highlightEnabled', config.ankiConnect.knownWords.highlightEnabled);
|
||||||
|
const nPlusOneColoringEnabled = getRuntimeBooleanOption('subtitle.annotation.nPlusOne', config.ankiConnect.nPlusOne.enabled);
|
||||||
return {
|
return {
|
||||||
enableKnownWordColoring: knownAndNPlusOneEnabled,
|
enableKnownWordColoring: knownWordColoringEnabled,
|
||||||
enableNPlusOneColoring: knownAndNPlusOneEnabled,
|
enableNPlusOneColoring: nPlusOneColoringEnabled,
|
||||||
enableNameMatchColoring: config.subtitleStyle.nameMatchEnabled && characterDictionaryEnabled,
|
enableNameMatchColoring: config.subtitleStyle.nameMatchEnabled && characterDictionaryEnabled,
|
||||||
enableFrequencyColoring: getRuntimeBooleanOption('subtitle.annotation.frequency', config.subtitleStyle.frequencyDictionary.enabled),
|
enableFrequencyColoring: getRuntimeBooleanOption('subtitle.annotation.frequency', config.subtitleStyle.frequencyDictionary.enabled),
|
||||||
enableJlptColoring: getRuntimeBooleanOption('subtitle.annotation.jlpt', config.subtitleStyle.enableJlpt),
|
enableJlptColoring: getRuntimeBooleanOption('subtitle.annotation.jlpt', config.subtitleStyle.enableJlpt),
|
||||||
@@ -1819,10 +1820,11 @@ function getRuntimeBooleanOption(id, fallback) {
|
|||||||
}
|
}
|
||||||
function shouldInitializeMecabForAnnotations() {
|
function shouldInitializeMecabForAnnotations() {
|
||||||
const config = getResolvedConfig();
|
const config = getResolvedConfig();
|
||||||
|
const knownWordsEnabled = getRuntimeBooleanOption('subtitle.annotation.knownWords.highlightEnabled', config.ankiConnect.knownWords.highlightEnabled);
|
||||||
const nPlusOneEnabled = getRuntimeBooleanOption('subtitle.annotation.nPlusOne', config.ankiConnect.nPlusOne.enabled);
|
const nPlusOneEnabled = getRuntimeBooleanOption('subtitle.annotation.nPlusOne', config.ankiConnect.nPlusOne.enabled);
|
||||||
const jlptEnabled = getRuntimeBooleanOption('subtitle.annotation.jlpt', config.subtitleStyle.enableJlpt);
|
const jlptEnabled = getRuntimeBooleanOption('subtitle.annotation.jlpt', config.subtitleStyle.enableJlpt);
|
||||||
const frequencyEnabled = getRuntimeBooleanOption('subtitle.annotation.frequency', config.subtitleStyle.frequencyDictionary.enabled);
|
const frequencyEnabled = getRuntimeBooleanOption('subtitle.annotation.frequency', config.subtitleStyle.frequencyDictionary.enabled);
|
||||||
return nPlusOneEnabled || jlptEnabled || frequencyEnabled;
|
return knownWordsEnabled || nPlusOneEnabled || jlptEnabled || frequencyEnabled;
|
||||||
}
|
}
|
||||||
const { getResolvedJellyfinConfig, reportJellyfinRemoteProgress, reportJellyfinRemoteStopped, startJellyfinRemoteSession, stopJellyfinRemoteSession, runJellyfinCommand, openJellyfinSetupWindow, getJellyfinClientInfo, } = (0, composers_1.composeJellyfinRuntimeHandlers)({
|
const { getResolvedJellyfinConfig, reportJellyfinRemoteProgress, reportJellyfinRemoteStopped, startJellyfinRemoteSession, stopJellyfinRemoteSession, runJellyfinCommand, openJellyfinSetupWindow, getJellyfinClientInfo, } = (0, composers_1.composeJellyfinRuntimeHandlers)({
|
||||||
getResolvedJellyfinConfigMainDeps: {
|
getResolvedJellyfinConfigMainDeps: {
|
||||||
@@ -4706,4 +4708,4 @@ function appendClipboardVideoToQueue() {
|
|||||||
return appendClipboardVideoToQueueHandler();
|
return appendClipboardVideoToQueueHandler();
|
||||||
}
|
}
|
||||||
registerIpcRuntimeHandlers();
|
registerIpcRuntimeHandlers();
|
||||||
//# sourceMappingURL=main.js.map
|
//# sourceMappingURL=main.js.map
|
||||||
|
|||||||
+2
-2
@@ -49,8 +49,8 @@
|
|||||||
"test:plugin:src": "lua scripts/test-plugin-lua-compat.lua && lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-session-bindings.lua && lua scripts/test-plugin-binary-windows.lua",
|
"test:plugin:src": "lua scripts/test-plugin-lua-compat.lua && lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-session-bindings.lua && lua scripts/test-plugin-binary-windows.lua",
|
||||||
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
||||||
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
||||||
"test:core:src": "bun test src/preload-settings.test.ts src/settings/settings-anki-controls.test.ts src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/yomitan-extension-loader.test.ts src/core/services/yomitan-settings.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/__tests__/stats-server.test.ts src/main/runtime/stats-server-routing.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/main/runtime/current-subtitle-snapshot.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/startup-mode-flags.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/command-line-launcher.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/main/runtime/update/appimage-updater.test.ts src/main/runtime/update/fetch-adapter.test.ts src/main/runtime/update/release-metadata-policy.test.ts src/main/runtime/update/update-dialogs.test.ts src/main/runtime/update/support-assets.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts stats/src/hooks/useExcludedWords.test.ts",
|
"test:core:src": "bun test src/preload-settings.test.ts src/settings/settings-anki-controls.test.ts src/settings/settings-model.test.ts src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/ipc.test.ts src/core/services/anki-jimaku-ipc.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/yomitan-extension-loader.test.ts src/core/services/yomitan-settings.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/__tests__/stats-server.test.ts src/main/runtime/stats-server-routing.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/main/runtime/current-subtitle-snapshot.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/startup-mode-flags.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/command-line-launcher.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/main/runtime/update/appimage-updater.test.ts src/main/runtime/update/fetch-adapter.test.ts src/main/runtime/update/release-metadata-policy.test.ts src/main/runtime/update/update-dialogs.test.ts src/main/runtime/update/support-assets.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts stats/src/hooks/useExcludedWords.test.ts",
|
||||||
"test:core:dist": "bun test dist/preload-settings.test.js dist/settings/settings-anki-controls.test.js dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/yomitan-extension-paths.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jimaku-download-path.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/overlay-runtime-init.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/main/runtime/current-subtitle-snapshot.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/core/services/anilist/rate-limiter.test.js dist/core/services/jlpt-token-filter.test.js dist/core/services/subtitle-position.test.js dist/renderer/error-recovery.test.js dist/renderer/subtitle-render.test.js dist/renderer/handlers/mouse.test.js dist/renderer/handlers/keyboard.test.js dist/renderer/modals/jimaku.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/hyprland-tracker.test.js dist/window-trackers/x11-tracker.test.js dist/window-trackers/windows-helper.test.js dist/window-trackers/windows-tracker.test.js",
|
"test:core:dist": "bun test dist/preload-settings.test.js dist/settings/settings-anki-controls.test.js dist/settings/settings-model.test.js dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/yomitan-extension-paths.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jimaku-download-path.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/overlay-runtime-init.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/main/runtime/current-subtitle-snapshot.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/core/services/anilist/rate-limiter.test.js dist/core/services/jlpt-token-filter.test.js dist/core/services/subtitle-position.test.js dist/renderer/error-recovery.test.js dist/renderer/subtitle-render.test.js dist/renderer/handlers/mouse.test.js dist/renderer/handlers/keyboard.test.js dist/renderer/modals/jimaku.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/hyprland-tracker.test.js dist/window-trackers/x11-tracker.test.js dist/window-trackers/windows-helper.test.js dist/window-trackers/windows-tracker.test.js",
|
||||||
"test:core:smoke:dist": "bun test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
"test:core:smoke:dist": "bun test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||||
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
|
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
|
||||||
"test:subtitle:src": "bun test src/core/services/subsync.test.ts src/subsync/utils.test.ts",
|
"test:subtitle:src": "bun test src/core/services/subsync.test.ts src/subsync/utils.test.ts",
|
||||||
|
|||||||
@@ -504,7 +504,14 @@ function M.create(ctx)
|
|||||||
subminer_log("info", "process", "Restarting overlay...")
|
subminer_log("info", "process", "Restarting overlay...")
|
||||||
show_osd("Restarting...")
|
show_osd("Restarting...")
|
||||||
|
|
||||||
run_control_command_async("stop", nil, function()
|
run_control_command_async("stop", nil, function(ok, result)
|
||||||
|
if not ok then
|
||||||
|
local reason = result and result.stderr or "unknown error"
|
||||||
|
subminer_log("warn", "process", "Restart stop command failed: " .. reason)
|
||||||
|
show_osd("Restart failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
state.overlay_running = false
|
state.overlay_running = false
|
||||||
state.texthooker_running = false
|
state.texthooker_running = false
|
||||||
disarm_auto_play_ready_gate()
|
disarm_auto_play_ready_gate()
|
||||||
|
|||||||
@@ -108,6 +108,13 @@ local function run_plugin_scenario(config)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
for _, value in ipairs(args) do
|
||||||
|
if value == "--stop" and config.stop_command_fails then
|
||||||
|
local stderr = config.stop_command_stderr or "stop failed"
|
||||||
|
callback(false, { status = 1, stdout = "", stderr = stderr }, stderr)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
callback(true, { status = 0, stdout = "", stderr = "" }, nil)
|
callback(true, { status = 0, stdout = "", stderr = "" }, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -593,6 +600,28 @@ do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local recorded, err = run_plugin_scenario({
|
||||||
|
process_list = "",
|
||||||
|
stop_command_fails = true,
|
||||||
|
stop_command_stderr = "stop refused",
|
||||||
|
option_overrides = {
|
||||||
|
binary_path = binary_path,
|
||||||
|
},
|
||||||
|
files = {
|
||||||
|
[binary_path] = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert_true(recorded ~= nil, "plugin failed to load for failed restart-stop scenario: " .. tostring(err))
|
||||||
|
recorded.script_messages["subminer-restart"]()
|
||||||
|
assert_true(find_control_call(recorded.async_calls, "--stop") ~= nil, "restart should attempt stop")
|
||||||
|
assert_true(count_start_calls(recorded.async_calls) == 0, "restart should not start overlay when stop fails")
|
||||||
|
assert_true(
|
||||||
|
has_osd_message(recorded.osd, "SubMiner: Restart failed"),
|
||||||
|
"restart stop failure should show failure OSD"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
local recorded, err = run_plugin_scenario({
|
local recorded, err = run_plugin_scenario({
|
||||||
process_list = "",
|
process_list = "",
|
||||||
|
|||||||
@@ -1842,6 +1842,7 @@ test('runtime options registry is centralized', () => {
|
|||||||
const ids = RUNTIME_OPTION_REGISTRY.map((entry) => entry.id);
|
const ids = RUNTIME_OPTION_REGISTRY.map((entry) => entry.id);
|
||||||
assert.deepEqual(ids, [
|
assert.deepEqual(ids, [
|
||||||
'anki.autoUpdateNewCards',
|
'anki.autoUpdateNewCards',
|
||||||
|
'subtitle.annotation.knownWords.highlightEnabled',
|
||||||
'subtitle.annotation.nPlusOne',
|
'subtitle.annotation.nPlusOne',
|
||||||
'subtitle.annotation.jlpt',
|
'subtitle.annotation.jlpt',
|
||||||
'subtitle.annotation.frequency',
|
'subtitle.annotation.frequency',
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export function buildRuntimeOptionRegistry(
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'subtitle.annotation.nPlusOne',
|
id: 'subtitle.annotation.knownWords.highlightEnabled',
|
||||||
path: 'ankiConnect.knownWords.highlightEnabled',
|
path: 'ankiConnect.knownWords.highlightEnabled',
|
||||||
label: 'N+1 Annotation',
|
label: 'Known Word Annotation',
|
||||||
scope: 'subtitle',
|
scope: 'subtitle',
|
||||||
valueType: 'boolean',
|
valueType: 'boolean',
|
||||||
allowedValues: [true, false],
|
allowedValues: [true, false],
|
||||||
@@ -35,6 +35,22 @@ export function buildRuntimeOptionRegistry(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'subtitle.annotation.nPlusOne',
|
||||||
|
path: 'ankiConnect.nPlusOne.enabled',
|
||||||
|
label: 'N+1 Annotation',
|
||||||
|
scope: 'subtitle',
|
||||||
|
valueType: 'boolean',
|
||||||
|
allowedValues: [true, false],
|
||||||
|
defaultValue: defaultConfig.ankiConnect.nPlusOne.enabled,
|
||||||
|
requiresRestart: false,
|
||||||
|
formatValueForOsd: (value) => (value === true ? 'On' : 'Off'),
|
||||||
|
toAnkiPatch: (value) => ({
|
||||||
|
nPlusOne: {
|
||||||
|
enabled: value === true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'subtitle.annotation.jlpt',
|
id: 'subtitle.annotation.jlpt',
|
||||||
path: 'subtitleStyle.enableJlpt',
|
path: 'subtitleStyle.enableJlpt',
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const JSON_OBJECT_FIELDS = new Set([
|
|||||||
|
|
||||||
const SECRET_PATHS = new Set(['ai.apiKey', 'jimaku.apiKey', 'anilist.accessToken']);
|
const SECRET_PATHS = new Set(['ai.apiKey', 'jimaku.apiKey', 'anilist.accessToken']);
|
||||||
|
|
||||||
const COLOR_SUFFIXES = new Set(['Color', 'color', 'backgroundColor', 'singleColor', 'nPlusOne']);
|
const COLOR_SUFFIXES = new Set(['Color', 'color', 'backgroundColor', 'singleColor']);
|
||||||
const SUBTITLE_CSS_MANAGED_CONFIG_PATHS = new Set([
|
const SUBTITLE_CSS_MANAGED_CONFIG_PATHS = new Set([
|
||||||
...getSubtitleCssManagedConfigPaths('primary'),
|
...getSubtitleCssManagedConfigPaths('primary'),
|
||||||
...getSubtitleCssManagedConfigPaths('secondary'),
|
...getSubtitleCssManagedConfigPaths('secondary'),
|
||||||
@@ -111,6 +111,9 @@ const CATEGORY_ORDER: ConfigSettingsCategory[] = [
|
|||||||
const SECTION_ORDER = new Map<string, number>(
|
const SECTION_ORDER = new Map<string, number>(
|
||||||
[
|
[
|
||||||
'Annotation Display',
|
'Annotation Display',
|
||||||
|
'Known Words',
|
||||||
|
'N+1',
|
||||||
|
'Frequency Highlighting',
|
||||||
'Primary Subtitle Appearance',
|
'Primary Subtitle Appearance',
|
||||||
'Secondary Subtitle Appearance',
|
'Secondary Subtitle Appearance',
|
||||||
'Subtitle Sidebar Appearance',
|
'Subtitle Sidebar Appearance',
|
||||||
|
|||||||
+17
-5
@@ -662,14 +662,18 @@ const texthookerService = new Texthooker(() => {
|
|||||||
const characterDictionaryEnabled =
|
const characterDictionaryEnabled =
|
||||||
config.anilist.characterDictionary.enabled &&
|
config.anilist.characterDictionary.enabled &&
|
||||||
yomitanProfilePolicy.isCharacterDictionaryEnabled();
|
yomitanProfilePolicy.isCharacterDictionaryEnabled();
|
||||||
const knownAndNPlusOneEnabled = getRuntimeBooleanOption(
|
const knownWordColoringEnabled = getRuntimeBooleanOption(
|
||||||
|
'subtitle.annotation.knownWords.highlightEnabled',
|
||||||
|
config.ankiConnect.knownWords.highlightEnabled,
|
||||||
|
);
|
||||||
|
const nPlusOneColoringEnabled = getRuntimeBooleanOption(
|
||||||
'subtitle.annotation.nPlusOne',
|
'subtitle.annotation.nPlusOne',
|
||||||
config.ankiConnect.nPlusOne.enabled,
|
config.ankiConnect.nPlusOne.enabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enableKnownWordColoring: knownAndNPlusOneEnabled,
|
enableKnownWordColoring: knownWordColoringEnabled,
|
||||||
enableNPlusOneColoring: knownAndNPlusOneEnabled,
|
enableNPlusOneColoring: nPlusOneColoringEnabled,
|
||||||
enableNameMatchColoring: config.subtitleStyle.nameMatchEnabled && characterDictionaryEnabled,
|
enableNameMatchColoring: config.subtitleStyle.nameMatchEnabled && characterDictionaryEnabled,
|
||||||
enableFrequencyColoring: getRuntimeBooleanOption(
|
enableFrequencyColoring: getRuntimeBooleanOption(
|
||||||
'subtitle.annotation.frequency',
|
'subtitle.annotation.frequency',
|
||||||
@@ -2571,7 +2575,11 @@ function getResolvedConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRuntimeBooleanOption(
|
function getRuntimeBooleanOption(
|
||||||
id: 'subtitle.annotation.nPlusOne' | 'subtitle.annotation.jlpt' | 'subtitle.annotation.frequency',
|
id:
|
||||||
|
| 'subtitle.annotation.knownWords.highlightEnabled'
|
||||||
|
| 'subtitle.annotation.nPlusOne'
|
||||||
|
| 'subtitle.annotation.jlpt'
|
||||||
|
| 'subtitle.annotation.frequency',
|
||||||
fallback: boolean,
|
fallback: boolean,
|
||||||
): boolean {
|
): boolean {
|
||||||
const value = appState.runtimeOptionsManager?.getOptionValue(id);
|
const value = appState.runtimeOptionsManager?.getOptionValue(id);
|
||||||
@@ -2580,6 +2588,10 @@ function getRuntimeBooleanOption(
|
|||||||
|
|
||||||
function shouldInitializeMecabForAnnotations(): boolean {
|
function shouldInitializeMecabForAnnotations(): boolean {
|
||||||
const config = getResolvedConfig();
|
const config = getResolvedConfig();
|
||||||
|
const knownWordsEnabled = getRuntimeBooleanOption(
|
||||||
|
'subtitle.annotation.knownWords.highlightEnabled',
|
||||||
|
config.ankiConnect.knownWords.highlightEnabled,
|
||||||
|
);
|
||||||
const nPlusOneEnabled = getRuntimeBooleanOption(
|
const nPlusOneEnabled = getRuntimeBooleanOption(
|
||||||
'subtitle.annotation.nPlusOne',
|
'subtitle.annotation.nPlusOne',
|
||||||
config.ankiConnect.nPlusOne.enabled,
|
config.ankiConnect.nPlusOne.enabled,
|
||||||
@@ -2592,7 +2604,7 @@ function shouldInitializeMecabForAnnotations(): boolean {
|
|||||||
'subtitle.annotation.frequency',
|
'subtitle.annotation.frequency',
|
||||||
config.subtitleStyle.frequencyDictionary.enabled,
|
config.subtitleStyle.frequencyDictionary.enabled,
|
||||||
);
|
);
|
||||||
return nPlusOneEnabled || jlptEnabled || frequencyEnabled;
|
return knownWordsEnabled || nPlusOneEnabled || jlptEnabled || frequencyEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ test('mpv input forwarding installs local key handling when session binding IPC
|
|||||||
testGlobals.setGetSessionBindings(() => new Promise<CompiledSessionBinding[]>(() => {}));
|
testGlobals.setGetSessionBindings(() => new Promise<CompiledSessionBinding[]>(() => {}));
|
||||||
const setupResult = await Promise.race([
|
const setupResult = await Promise.race([
|
||||||
handlers.setupMpvInputForwarding().then(() => 'resolved'),
|
handlers.setupMpvInputForwarding().then(() => 'resolved'),
|
||||||
wait(25).then(() => 'pending'),
|
wait(75).then(() => 'pending'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert.equal(setupResult, 'resolved');
|
assert.equal(setupResult, 'resolved');
|
||||||
@@ -533,6 +533,35 @@ test('mpv input forwarding installs local key handling when session binding IPC
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('mpv input forwarding waits for session bindings before resolving setup', async () => {
|
||||||
|
const { handlers, testGlobals } = createKeyboardHandlerHarness();
|
||||||
|
|
||||||
|
try {
|
||||||
|
testGlobals.setGetSessionBindings(async () => {
|
||||||
|
await wait(20);
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sourcePath: 'keybindings[0].key',
|
||||||
|
originalKey: 'KeyH',
|
||||||
|
key: { code: 'KeyH', modifiers: [] },
|
||||||
|
actionType: 'mpv-command',
|
||||||
|
command: ['cycle', 'pause'],
|
||||||
|
},
|
||||||
|
] as CompiledSessionBinding[];
|
||||||
|
});
|
||||||
|
|
||||||
|
await handlers.setupMpvInputForwarding();
|
||||||
|
|
||||||
|
assert.deepEqual(handlers.getSessionHelpOpeningInfo(), {
|
||||||
|
bindingKey: 'KeyK',
|
||||||
|
fallbackUsed: true,
|
||||||
|
fallbackUnavailable: false,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
testGlobals.restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('mpv input forwarding retries a transient keyboard config IPC failure', async () => {
|
test('mpv input forwarding retries a transient keyboard config IPC failure', async () => {
|
||||||
const { handlers, testGlobals } = createKeyboardHandlerHarness();
|
const { handlers, testGlobals } = createKeyboardHandlerHarness();
|
||||||
let calls = 0;
|
let calls = 0;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function createKeyboardHandlers(
|
|||||||
) {
|
) {
|
||||||
// Timeout for the modal chord capture window (e.g. Y followed by H/K).
|
// Timeout for the modal chord capture window (e.g. Y followed by H/K).
|
||||||
const CHORD_TIMEOUT_MS = 1000;
|
const CHORD_TIMEOUT_MS = 1000;
|
||||||
|
const MPV_INPUT_FORWARDING_CONFIG_LOAD_TIMEOUT_MS = 50;
|
||||||
const KEYBOARD_SELECTED_WORD_CLASS = 'keyboard-selected';
|
const KEYBOARD_SELECTED_WORD_CLASS = 'keyboard-selected';
|
||||||
let pendingSelectionAnchorAfterSubtitleSeek: 'start' | 'end' | null = null;
|
let pendingSelectionAnchorAfterSubtitleSeek: 'start' | 'end' | null = null;
|
||||||
let pendingLookupRefreshAfterSubtitleSeek = false;
|
let pendingLookupRefreshAfterSubtitleSeek = false;
|
||||||
@@ -975,26 +976,21 @@ export function createKeyboardHandlers(
|
|||||||
installMpvInputForwardingListeners();
|
installMpvInputForwardingListeners();
|
||||||
syncKeyboardTokenSelection();
|
syncKeyboardTokenSelection();
|
||||||
|
|
||||||
let configLoadSettled = false;
|
|
||||||
let configLoadError: unknown = null;
|
let configLoadError: unknown = null;
|
||||||
const configLoad = loadMpvInputForwardingConfigWithRetry().then(
|
const configLoad = loadMpvInputForwardingConfigWithRetry().then(
|
||||||
() => {
|
() => {},
|
||||||
configLoadSettled = true;
|
|
||||||
},
|
|
||||||
(error) => {
|
(error) => {
|
||||||
configLoadSettled = true;
|
|
||||||
configLoadError = error;
|
configLoadError = error;
|
||||||
console.error('Failed to load overlay keyboard configuration.', error);
|
console.error('Failed to load overlay keyboard configuration.', error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await Promise.race([
|
||||||
setTimeout(resolve, 0);
|
configLoad,
|
||||||
});
|
new Promise<void>((resolve) => {
|
||||||
if (!configLoadSettled) {
|
setTimeout(resolve, MPV_INPUT_FORWARDING_CONFIG_LOAD_TIMEOUT_MS);
|
||||||
void configLoad;
|
}),
|
||||||
return;
|
]);
|
||||||
}
|
|
||||||
if (configLoadError) {
|
if (configLoadError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,14 @@ test('applySidebarCssDeclarations clears declarations removed by config reload',
|
|||||||
assert.equal(style.color, '#ffffff');
|
assert.equal(style.color, '#ffffff');
|
||||||
assert.equal(style.backgroundColor, '');
|
assert.equal(style.backgroundColor, '');
|
||||||
assert.deepEqual(removed, ['background-color']);
|
assert.deepEqual(removed, ['background-color']);
|
||||||
|
|
||||||
|
applySidebarCssDeclarations(target, {
|
||||||
|
color: '',
|
||||||
|
'background-color': '',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(style.color, '');
|
||||||
|
assert.deepEqual(removed, ['background-color', 'background-color']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('subtitle sidebar modal opens from snapshot and clicking cue seeks playback', async () => {
|
test('subtitle sidebar modal opens from snapshot and clicking cue seeks playback', async () => {
|
||||||
|
|||||||
@@ -77,7 +77,14 @@ export function applySidebarCssDeclarations(
|
|||||||
|
|
||||||
for (const [property, rawValue] of Object.entries(declarations)) {
|
for (const [property, rawValue] of Object.entries(declarations)) {
|
||||||
const value = rawValue.trim();
|
const value = rawValue.trim();
|
||||||
if (value.length === 0) continue;
|
if (value.length === 0) {
|
||||||
|
if (property.includes('-')) {
|
||||||
|
targetStyle.removeProperty(property);
|
||||||
|
} else {
|
||||||
|
styleTarget[property] = '';
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (property.includes('-')) {
|
if (property.includes('-')) {
|
||||||
targetStyle.setProperty(property, value);
|
targetStyle.setProperty(property, value);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
|
|
||||||
|
import { DEFAULT_CONFIG } from './config';
|
||||||
import { RuntimeOptionsManager } from './runtime-options';
|
import { RuntimeOptionsManager } from './runtime-options';
|
||||||
|
|
||||||
test('SM-012 runtime options path does not use JSON serialize-clone helpers', () => {
|
test('SM-012 runtime options path does not use JSON serialize-clone helpers', () => {
|
||||||
@@ -59,3 +60,25 @@ test('RuntimeOptionsManager returns detached effective Anki config copies', () =
|
|||||||
assert.deepEqual(baseConfig.tags, ['SubMiner']);
|
assert.deepEqual(baseConfig.tags, ['SubMiner']);
|
||||||
assert.equal(baseConfig.behavior.autoUpdateNewCards, true);
|
assert.equal(baseConfig.behavior.autoUpdateNewCards, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('RuntimeOptionsManager keeps known-word and n+1 annotation toggles separate', () => {
|
||||||
|
const baseConfig = structuredClone(DEFAULT_CONFIG.ankiConnect);
|
||||||
|
const patches: unknown[] = [];
|
||||||
|
const manager = new RuntimeOptionsManager(() => structuredClone(baseConfig), {
|
||||||
|
applyAnkiPatch: (patch) => {
|
||||||
|
patches.push(patch);
|
||||||
|
},
|
||||||
|
onOptionsChanged: () => undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
manager.setOptionValue('subtitle.annotation.knownWords.highlightEnabled', true).ok,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert.equal(manager.setOptionValue('subtitle.annotation.nPlusOne', true).ok, true);
|
||||||
|
|
||||||
|
const effective = manager.getEffectiveAnkiConnectConfig();
|
||||||
|
assert.equal(effective.knownWords?.highlightEnabled, true);
|
||||||
|
assert.equal(effective.nPlusOne?.enabled, true);
|
||||||
|
assert.deepEqual(patches, []);
|
||||||
|
});
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ export function renderMpvKeybindingsInput(
|
|||||||
removeButton.type = 'button';
|
removeButton.type = 'button';
|
||||||
removeButton.textContent = 'Remove';
|
removeButton.textContent = 'Remove';
|
||||||
removeButton.addEventListener('click', () => {
|
removeButton.addEventListener('click', () => {
|
||||||
rows.splice(i, 1);
|
const nextRows = rows.filter((_, index) => index !== i);
|
||||||
applyMpvRows(context, field, rows);
|
applyMpvRows(context, field, nextRows);
|
||||||
requestRender();
|
requestRender();
|
||||||
});
|
});
|
||||||
item.append(keyButton, command, removeButton);
|
item.append(keyButton, command, removeButton);
|
||||||
|
|||||||
@@ -87,6 +87,27 @@ test('filterSettingsFields normalizes punctuation in query terms', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('filterSettingsFields preserves non-Latin query terms', () => {
|
||||||
|
const japaneseFields: ConfigSettingsField[] = [
|
||||||
|
{
|
||||||
|
id: 'subtitleStyle.japaneseFontFamily',
|
||||||
|
label: '日本語フォント',
|
||||||
|
description: '字幕の表示に使う書体。',
|
||||||
|
configPath: 'subtitleStyle.japaneseFontFamily',
|
||||||
|
category: 'appearance',
|
||||||
|
section: 'Primary Subtitle Appearance',
|
||||||
|
control: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
restartBehavior: 'hot-reload',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
filterSettingsFields(japaneseFields, { query: '日本語' }).map((field) => field.configPath),
|
||||||
|
['subtitleStyle.japaneseFontFamily'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('settings draft tracks dirty set and emits save operations', () => {
|
test('settings draft tracks dirty set and emits save operations', () => {
|
||||||
const draft = createSettingsDraft({
|
const draft = createSettingsDraft({
|
||||||
'subtitleStyle.autoPauseVideoOnHover': true,
|
'subtitleStyle.autoPauseVideoOnHover': true,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface SettingsDraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeQuery(query: string | undefined): string {
|
function normalizeQuery(query: string | undefined): string {
|
||||||
return (query ?? '').trim().toLowerCase();
|
return (query ?? '').trim().toLocaleLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchableText(parts: Array<string | undefined>): string {
|
function searchableText(parts: Array<string | undefined>): string {
|
||||||
@@ -25,8 +25,8 @@ function searchableText(parts: Array<string | undefined>): string {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')
|
.join(' ')
|
||||||
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
||||||
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
||||||
.toLowerCase();
|
.toLocaleLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function valuesEqual(a: unknown, b: unknown): boolean {
|
function valuesEqual(a: unknown, b: unknown): boolean {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const SESSION_ACTION_IDS: SessionActionId[] = [
|
|||||||
|
|
||||||
const RUNTIME_OPTION_IDS: RuntimeOptionId[] = [
|
const RUNTIME_OPTION_IDS: RuntimeOptionId[] = [
|
||||||
'anki.autoUpdateNewCards',
|
'anki.autoUpdateNewCards',
|
||||||
|
'subtitle.annotation.knownWords.highlightEnabled',
|
||||||
'subtitle.annotation.nPlusOne',
|
'subtitle.annotation.nPlusOne',
|
||||||
'subtitle.annotation.jlpt',
|
'subtitle.annotation.jlpt',
|
||||||
'subtitle.annotation.frequency',
|
'subtitle.annotation.frequency',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export type RuntimeOptionId =
|
export type RuntimeOptionId =
|
||||||
| 'anki.autoUpdateNewCards'
|
| 'anki.autoUpdateNewCards'
|
||||||
|
| 'subtitle.annotation.knownWords.highlightEnabled'
|
||||||
| 'subtitle.annotation.nPlusOne'
|
| 'subtitle.annotation.nPlusOne'
|
||||||
| 'subtitle.annotation.jlpt'
|
| 'subtitle.annotation.jlpt'
|
||||||
| 'subtitle.annotation.frequency'
|
| 'subtitle.annotation.frequency'
|
||||||
|
|||||||
Reference in New Issue
Block a user