docs: remove legacy option refs and modernize config docs (#89)

This commit is contained in:
2026-05-26 18:15:23 -07:00
committed by GitHub
parent 2add95d541
commit 5b44981688
13 changed files with 162 additions and 233 deletions
+2 -2
View File
@@ -2241,7 +2241,7 @@ test('resolves legacy ankiConnect nPlusOne known-word settings without rewriting
const warnings = service.getWarnings();
assert.equal(config.ankiConnect.knownWords.highlightEnabled, true);
assert.equal(config.ankiConnect.nPlusOne.enabled, true);
assert.equal(config.ankiConnect.nPlusOne.enabled, DEFAULT_CONFIG.ankiConnect.nPlusOne.enabled);
assert.equal(config.ankiConnect.knownWords.refreshMinutes, 90);
assert.equal(config.ankiConnect.knownWords.matchMode, 'surface');
assert.deepEqual(config.ankiConnect.knownWords.decks, {
@@ -2300,7 +2300,7 @@ test('supports legacy ankiConnect.behavior N+1 settings as fallback', () => {
const warnings = service.getWarnings();
assert.equal(config.ankiConnect.knownWords.highlightEnabled, true);
assert.equal(config.ankiConnect.nPlusOne.enabled, true);
assert.equal(config.ankiConnect.nPlusOne.enabled, DEFAULT_CONFIG.ankiConnect.nPlusOne.enabled);
assert.equal(config.ankiConnect.knownWords.refreshMinutes, 90);
assert.equal(config.ankiConnect.knownWords.matchMode, 'surface');
assert.ok(
+10 -3
View File
@@ -35,9 +35,7 @@ function collectConfigLeafPaths(config: ResolvedConfig): string[] {
}
// DEFAULT_CONFIG leaves that intentionally do not have a curated
// CONFIG_OPTION_REGISTRY entry. The generated config.example.jsonc still
// includes these paths, but their inline comments fall back to an auto-
// humanized key name instead of a written description.
// CONFIG_OPTION_REGISTRY entry.
//
// Current intentional gaps:
// - subtitleStyle.*: thin wrappers around standard CSS properties; the
@@ -49,6 +47,8 @@ function collectConfigLeafPaths(config: ResolvedConfig): string[] {
// an allowlist entry. Only allowlist a path when the registry is genuinely
// the wrong surface for it.
const UNDOCUMENTED_LEAVES: ReadonlySet<string> = new Set([
'anilist.characterDictionary.evictionPolicy',
'anilist.characterDictionary.refreshTtlHours',
'keybindings',
'subtitleStyle.backdropFilter',
'subtitleStyle.backgroundColor',
@@ -85,6 +85,13 @@ const UNDOCUMENTED_LEAVES: ReadonlySet<string> = new Set([
'subtitleStyle.textShadow',
'subtitleStyle.WebkitTextStroke',
'subtitleStyle.wordSpacing',
'youtubeSubgen.ai.model',
'youtubeSubgen.ai.systemPrompt',
'youtubeSubgen.fixWithAi',
'youtubeSubgen.whisperBin',
'youtubeSubgen.whisperModel',
'youtubeSubgen.whisperThreads',
'youtubeSubgen.whisperVadModel',
]);
test('config option registry includes critical paths and has unique entries', () => {
+1 -1
View File
@@ -188,7 +188,7 @@ export function buildCoreConfigOptionRegistry(
kind: 'object',
defaultValue: defaultConfig.controller.buttonIndices,
description:
'Semantic button-name reference mapping used for legacy configs and debug output. Updating it does not rewrite existing raw binding descriptors.',
'Semantic button-name reference mapping used for debug output. Updating it does not rewrite existing raw binding descriptors.',
},
{
path: 'controller.buttonIndices.select',
+1 -64
View File
@@ -304,7 +304,7 @@ export function buildIntegrationConfigOptionRegistry(
kind: 'object',
defaultValue: defaultConfig.ankiConnect.knownWords.decks,
description:
'Decks and fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word", "Word Reading"] }.',
'Decks and expression/word fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word"] }.',
},
{
path: 'ankiConnect.isKiku.fieldGrouping',
@@ -392,13 +392,6 @@ export function buildIntegrationConfigOptionRegistry(
description:
'Optional explicit AniList access token override; leave empty to use locally stored token from setup.',
},
{
path: 'anilist.characterDictionary.refreshTtlHours',
kind: 'number',
defaultValue: defaultConfig.anilist.characterDictionary.refreshTtlHours,
description:
'Legacy setting; merged character dictionary retention is now usage-based and this value is ignored.',
},
{
path: 'anilist.characterDictionary.maxLoaded',
kind: 'number',
@@ -406,14 +399,6 @@ export function buildIntegrationConfigOptionRegistry(
description:
'Maximum number of most-recently-used anime snapshots included in the merged Yomitan character dictionary.',
},
{
path: 'anilist.characterDictionary.evictionPolicy',
kind: 'enum',
enumValues: ['disable', 'delete'],
defaultValue: defaultConfig.anilist.characterDictionary.evictionPolicy,
description:
'Legacy setting; merged character dictionary eviction is usage-based and this value is ignored.',
},
{
path: 'anilist.characterDictionary.profileScope',
kind: 'enum',
@@ -665,53 +650,5 @@ export function buildIntegrationConfigOptionRegistry(
defaultValue: defaultConfig.ai.requestTimeoutMs,
description: 'Timeout in milliseconds for shared AI provider requests.',
},
{
path: 'youtubeSubgen.whisperBin',
kind: 'string',
defaultValue: defaultConfig.youtubeSubgen.whisperBin,
description:
'Legacy compatibility path kept for external subtitle fallback tools; not used by default.',
},
{
path: 'youtubeSubgen.whisperModel',
kind: 'string',
defaultValue: defaultConfig.youtubeSubgen.whisperModel,
description:
'Legacy compatibility model path kept for external subtitle fallback tooling; not used by default.',
},
{
path: 'youtubeSubgen.whisperVadModel',
kind: 'string',
defaultValue: defaultConfig.youtubeSubgen.whisperVadModel,
description:
'Legacy compatibility VAD path kept for external subtitle fallback tooling; not used by default.',
},
{
path: 'youtubeSubgen.whisperThreads',
kind: 'number',
defaultValue: defaultConfig.youtubeSubgen.whisperThreads,
description: 'Legacy thread tuning for subtitle fallback tooling; not used by default.',
},
{
path: 'youtubeSubgen.fixWithAi',
kind: 'boolean',
defaultValue: defaultConfig.youtubeSubgen.fixWithAi,
description:
'Legacy subtitle fallback post-processing switch kept for compatibility; use is currently disabled by default.',
},
{
path: 'youtubeSubgen.ai.model',
kind: 'string',
defaultValue: defaultConfig.youtubeSubgen.ai.model,
description:
'Optional model override for legacy subtitle fallback post-processing; not used by default.',
},
{
path: 'youtubeSubgen.ai.systemPrompt',
kind: 'string',
defaultValue: defaultConfig.youtubeSubgen.ai.systemPrompt,
description:
'Optional system prompt override for legacy subtitle fallback post-processing; not used by default.',
},
];
}
+3 -3
View File
@@ -84,7 +84,7 @@ test('accepts knownWords.addMinedWordsImmediately boolean override', () => {
);
});
test('enables n+1 for existing configs with known-word highlighting enabled', () => {
test('knownWords.highlightEnabled does not implicitly enable nPlusOne', () => {
const { context } = makeContext({
knownWords: { highlightEnabled: true },
});
@@ -92,10 +92,10 @@ test('enables n+1 for existing configs with known-word highlighting enabled', ()
applyAnkiConnectResolution(context);
assert.equal(context.resolved.ankiConnect.knownWords.highlightEnabled, true);
assert.equal(context.resolved.ankiConnect.nPlusOne.enabled, true);
assert.equal(context.resolved.ankiConnect.nPlusOne.enabled, DEFAULT_CONFIG.ankiConnect.nPlusOne.enabled);
});
test('keeps explicit n+1 disabled when known-word highlighting is enabled', () => {
test('explicit nPlusOne.enabled is respected regardless of highlightEnabled', () => {
const { context } = makeContext({
knownWords: { highlightEnabled: true },
nPlusOne: { enabled: false },
-2
View File
@@ -758,8 +758,6 @@ export function applyAnkiConnectResolution(context: ResolveContext): void {
'Expected boolean.',
);
context.resolved.ankiConnect.nPlusOne.enabled = DEFAULT_CONFIG.ankiConnect.nPlusOne.enabled;
} else if (context.resolved.ankiConnect.knownWords.highlightEnabled === true) {
context.resolved.ankiConnect.nPlusOne.enabled = true;
} else {
context.resolved.ankiConnect.nPlusOne.enabled = DEFAULT_CONFIG.ankiConnect.nPlusOne.enabled;
}
+14
View File
@@ -18,6 +18,17 @@ const TOP_LEVEL_SECTION_DESCRIPTION_BY_KEY = new Map(
CONFIG_TEMPLATE_SECTIONS.map((section) => [String(section.key), section.description[0] ?? '']),
);
const SUBTITLE_CSS_SCOPES: SubtitleCssScope[] = ['primary', 'secondary', 'sidebar'];
const HIDDEN_TEMPLATE_PATHS = [
'anilist.characterDictionary.evictionPolicy',
'anilist.characterDictionary.refreshTtlHours',
'youtubeSubgen.ai.model',
'youtubeSubgen.ai.systemPrompt',
'youtubeSubgen.fixWithAi',
'youtubeSubgen.whisperBin',
'youtubeSubgen.whisperModel',
'youtubeSubgen.whisperThreads',
'youtubeSubgen.whisperVadModel',
];
function normalizeCommentText(value: string): string {
return value.replace(/\s+/g, ' ').replace(/\*\//g, '*\\/').trim();
@@ -172,6 +183,9 @@ function renderSection(
function createTemplateConfig(config: ResolvedConfig): ResolvedConfig {
const templateConfig = deepCloneConfig(config);
foldSubtitleCssManagedDefaults(templateConfig);
for (const hiddenPath of HIDDEN_TEMPLATE_PATHS) {
deleteValueAtPath(templateConfig, hiddenPath);
}
if (templateConfig.keybindings.length === 0) {
templateConfig.keybindings = DEFAULT_KEYBINDINGS.map((binding) => ({
key: binding.key,