fix(config): align live save feedback with hot reload policy (#255)

This commit is contained in:
2026-09-20 19:51:45 -07:00
committed by GitHub
parent 4f0762e840
commit 76e43d7e6d
8 changed files with 205 additions and 137 deletions
+64
View File
@@ -0,0 +1,64 @@
function pathStartsWith(path: string, prefix: string): boolean {
return path === prefix || path.startsWith(`${prefix}.`);
}
const HOT_RELOAD_ROOTS = ['subtitleStyle', 'keybindings', 'shortcuts', 'subtitleSidebar'] as const;
const HOT_RELOAD_EXACT_OR_PREFIX_PATHS = [
'secondarySub.defaultMode',
'mpv.aniskipEnabled',
'mpv.aniskipButtonKey',
'ankiConnect.ai.enabled',
'stats.toggleKey',
'stats.markWatchedKey',
'logging.level',
'logging.rotation',
'logging.files',
'youtube.primarySubLanguages',
'ankiConnect.deck',
'ankiConnect.media.normalizeAudio',
'ankiConnect.media.mirrorMpvVolume',
'ankiConnect.media.reviewTiming',
'ankiConnect.behavior.autoUpdateNewCards',
'ankiConnect.knownWords.highlightEnabled',
'ankiConnect.knownWords.refreshMinutes',
'ankiConnect.knownWords.addMinedWordsImmediately',
'ankiConnect.knownWords.matchMode',
'ankiConnect.knownWords.decks',
'ankiConnect.nPlusOne.enabled',
'ankiConnect.nPlusOne.minSentenceWords',
'ankiConnect.fields.word',
'ankiConnect.fields.audio',
'ankiConnect.fields.image',
'ankiConnect.fields.sentence',
'ankiConnect.fields.miscInfo',
'ankiConnect.isLapis.sentenceCardModel',
'ankiConnect.isKiku.fieldGrouping',
'ankiConnect.isSenren.fieldGrouping',
'ankiConnect.lapisKiku.wordCardKind',
] as const;
export function getConfigHotReloadField(path: string): string | null {
for (const root of HOT_RELOAD_ROOTS) {
if (pathStartsWith(path, root)) {
return root;
}
}
for (const hotPath of HOT_RELOAD_EXACT_OR_PREFIX_PATHS) {
if (pathStartsWith(path, hotPath)) {
return hotPath;
}
}
// These consumers read the current config when the next operation starts.
if (
['jimaku', 'subsync', 'notifications', 'subtitleGeneration'].some((root) =>
pathStartsWith(path, root),
)
) {
return path;
}
return null;
}
+2 -49
View File
@@ -1,9 +1,9 @@
import { getConfigHotReloadField } from '../hot-reload';
import type { ResolvedConfig } from '../../types/config';
import type {
ConfigSettingsCategory,
ConfigSettingsControl,
ConfigSettingsField,
ConfigSettingsRestartBehavior,
} from '../../types/settings';
import { CONFIG_OPTION_REGISTRY, DEFAULT_CONFIG } from '../definitions';
import {
@@ -693,53 +693,6 @@ function compareFields(a: ConfigSettingsField, b: ConfigSettingsField): number {
return a.configPath.localeCompare(b.configPath);
}
function restartBehaviorForPath(path: string): ConfigSettingsRestartBehavior {
if (
path === 'keybindings' ||
pathStartsWith(path, 'shortcuts') ||
pathStartsWith(path, 'subtitleStyle') ||
pathStartsWith(path, 'subtitleSidebar') ||
path === 'secondarySub.defaultMode' ||
path === 'ankiConnect.deck' ||
path === 'ankiConnect.ai.enabled' ||
path === 'ankiConnect.media.normalizeAudio' ||
path === 'ankiConnect.media.mirrorMpvVolume' ||
path === 'ankiConnect.media.reviewTiming' ||
path === 'ankiConnect.behavior.autoUpdateNewCards' ||
path === 'ankiConnect.knownWords.highlightEnabled' ||
path === 'ankiConnect.knownWords.refreshMinutes' ||
path === 'ankiConnect.knownWords.addMinedWordsImmediately' ||
path === 'ankiConnect.knownWords.matchMode' ||
path === 'ankiConnect.knownWords.decks' ||
path === 'ankiConnect.nPlusOne.enabled' ||
path === 'ankiConnect.nPlusOne.minSentenceWords' ||
path === 'ankiConnect.fields.word' ||
path === 'ankiConnect.fields.audio' ||
path === 'ankiConnect.fields.image' ||
path === 'ankiConnect.fields.sentence' ||
path === 'ankiConnect.fields.miscInfo' ||
path === 'ankiConnect.isLapis.sentenceCardModel' ||
path === 'ankiConnect.isKiku.fieldGrouping' ||
path === 'ankiConnect.isSenren.fieldGrouping' ||
path === 'ankiConnect.lapisKiku.wordCardKind' ||
path === 'mpv.aniskipEnabled' ||
path === 'mpv.aniskipButtonKey' ||
path === 'stats.toggleKey' ||
path === 'stats.markWatchedKey' ||
path === 'logging.level' ||
path === 'logging.rotation' ||
pathStartsWith(path, 'logging.files') ||
pathStartsWith(path, 'notifications') ||
path === 'youtube.primarySubLanguages' ||
pathStartsWith(path, 'jimaku') ||
pathStartsWith(path, 'subsync') ||
pathStartsWith(path, 'subtitleGeneration')
) {
return 'hot-reload';
}
return 'restart';
}
function fieldForLeaf(leaf: Leaf): ConfigSettingsField {
const option = OPTION_BY_PATH.get(leaf.path);
const { category, section } = categoryAndSection(leaf.path);
@@ -758,7 +711,7 @@ function fieldForLeaf(leaf: Leaf): ConfigSettingsField {
? { enumValues: option.settingsEnumValues ?? option.enumValues }
: {}),
...(option?.enumLabels ? { enumLabels: option.enumLabels } : {}),
restartBehavior: restartBehaviorForPath(leaf.path),
restartBehavior: getConfigHotReloadField(leaf.path) ? 'hot-reload' : 'restart',
advanced:
leaf.path.startsWith('controller.') ||
leaf.path.startsWith('immersionTracking.retention.') ||