mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 06:22:44 -08:00
Add vendor dict fallback logic
This commit is contained in:
@@ -195,6 +195,20 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
|
||||
N4: "#a6e3a1",
|
||||
N5: "#8aadf4",
|
||||
},
|
||||
frequencyDictionary: {
|
||||
enabled: false,
|
||||
sourcePath: "",
|
||||
topX: 1000,
|
||||
mode: "single",
|
||||
singleColor: "#f5a97f",
|
||||
bandedColors: [
|
||||
"#ed8796",
|
||||
"#f5a97f",
|
||||
"#f9e2af",
|
||||
"#a6e3a1",
|
||||
"#8aadf4",
|
||||
],
|
||||
},
|
||||
secondary: {
|
||||
fontSize: 24,
|
||||
fontColor: "#ffffff",
|
||||
@@ -306,6 +320,48 @@ export const CONFIG_OPTION_REGISTRY: ConfigOptionRegistryEntry[] = [
|
||||
description: "Enable JLPT vocabulary level underlines. "
|
||||
+ "When disabled, JLPT tagging lookup and underlines are skipped.",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.enabled",
|
||||
kind: "boolean",
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.enabled,
|
||||
description:
|
||||
"Enable frequency-dictionary-based highlighting based on token rank.",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.sourcePath",
|
||||
kind: "string",
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.sourcePath,
|
||||
description:
|
||||
"Optional absolute path to a frequency dictionary directory."
|
||||
+ " If empty, built-in discovery search paths are used.",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.topX",
|
||||
kind: "number",
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.topX,
|
||||
description: "Only color tokens with frequency rank <= topX (default: 1000).",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.mode",
|
||||
kind: "enum",
|
||||
enumValues: ["single", "banded"],
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.mode,
|
||||
description:
|
||||
"single: use one color for all matching tokens. banded: use color ramp by frequency band.",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.singleColor",
|
||||
kind: "string",
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.singleColor,
|
||||
description: "Color used when frequencyDictionary.mode is `single`.",
|
||||
},
|
||||
{
|
||||
path: "subtitleStyle.frequencyDictionary.bandedColors",
|
||||
kind: "array",
|
||||
defaultValue: DEFAULT_CONFIG.subtitleStyle.frequencyDictionary.bandedColors,
|
||||
description:
|
||||
"Five colors used for rank bands when mode is `banded` (from most common to least within topX).",
|
||||
},
|
||||
{
|
||||
path: "ankiConnect.enabled",
|
||||
kind: "boolean",
|
||||
|
||||
@@ -45,6 +45,21 @@ function asColor(value: unknown): string | undefined {
|
||||
return hexColorPattern.test(text) ? text : undefined;
|
||||
}
|
||||
|
||||
function asFrequencyBandedColors(
|
||||
value: unknown,
|
||||
): [string, string, string, string, string] | undefined {
|
||||
if (!Array.isArray(value) || value.length !== 5) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const colors = value.map((item) => asColor(item));
|
||||
if (colors.some((color) => color === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return colors as [string, string, string, string, string];
|
||||
}
|
||||
|
||||
export class ConfigService {
|
||||
private readonly configDir: string;
|
||||
private readonly configFileJsonc: string;
|
||||
@@ -468,6 +483,108 @@ export class ConfigService {
|
||||
"Expected boolean.",
|
||||
);
|
||||
}
|
||||
|
||||
const frequencyDictionary = isObject(
|
||||
(src.subtitleStyle as { frequencyDictionary?: unknown })
|
||||
.frequencyDictionary,
|
||||
)
|
||||
? ((src.subtitleStyle as { frequencyDictionary?: unknown })
|
||||
.frequencyDictionary as Record<string, unknown>)
|
||||
: {};
|
||||
const frequencyEnabled = asBoolean(
|
||||
(frequencyDictionary as { enabled?: unknown }).enabled,
|
||||
);
|
||||
if (frequencyEnabled !== undefined) {
|
||||
resolved.subtitleStyle.frequencyDictionary.enabled = frequencyEnabled;
|
||||
} else if (
|
||||
(frequencyDictionary as { enabled?: unknown }).enabled !== undefined
|
||||
) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.enabled",
|
||||
(frequencyDictionary as { enabled?: unknown }).enabled,
|
||||
resolved.subtitleStyle.frequencyDictionary.enabled,
|
||||
"Expected boolean.",
|
||||
);
|
||||
}
|
||||
|
||||
const sourcePath = asString(
|
||||
(frequencyDictionary as { sourcePath?: unknown }).sourcePath,
|
||||
);
|
||||
if (sourcePath !== undefined) {
|
||||
resolved.subtitleStyle.frequencyDictionary.sourcePath = sourcePath;
|
||||
} else if (
|
||||
(frequencyDictionary as { sourcePath?: unknown }).sourcePath !== undefined
|
||||
) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.sourcePath",
|
||||
(frequencyDictionary as { sourcePath?: unknown }).sourcePath,
|
||||
resolved.subtitleStyle.frequencyDictionary.sourcePath,
|
||||
"Expected string.",
|
||||
);
|
||||
}
|
||||
|
||||
const topX = asNumber((frequencyDictionary as { topX?: unknown }).topX);
|
||||
if (
|
||||
topX !== undefined &&
|
||||
Number.isInteger(topX) &&
|
||||
topX > 0
|
||||
) {
|
||||
resolved.subtitleStyle.frequencyDictionary.topX = Math.floor(topX);
|
||||
} else if ((frequencyDictionary as { topX?: unknown }).topX !== undefined) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.topX",
|
||||
(frequencyDictionary as { topX?: unknown }).topX,
|
||||
resolved.subtitleStyle.frequencyDictionary.topX,
|
||||
"Expected a positive integer.",
|
||||
);
|
||||
}
|
||||
|
||||
const frequencyMode = frequencyDictionary.mode;
|
||||
if (
|
||||
frequencyMode === "single" ||
|
||||
frequencyMode === "banded"
|
||||
) {
|
||||
resolved.subtitleStyle.frequencyDictionary.mode = frequencyMode;
|
||||
} else if (frequencyMode !== undefined) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.mode",
|
||||
frequencyDictionary.mode,
|
||||
resolved.subtitleStyle.frequencyDictionary.mode,
|
||||
"Expected 'single' or 'banded'.",
|
||||
);
|
||||
}
|
||||
|
||||
const singleColor = asColor(
|
||||
(frequencyDictionary as { singleColor?: unknown }).singleColor,
|
||||
);
|
||||
if (singleColor !== undefined) {
|
||||
resolved.subtitleStyle.frequencyDictionary.singleColor = singleColor;
|
||||
} else if (
|
||||
(frequencyDictionary as { singleColor?: unknown }).singleColor !== undefined
|
||||
) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.singleColor",
|
||||
(frequencyDictionary as { singleColor?: unknown }).singleColor,
|
||||
resolved.subtitleStyle.frequencyDictionary.singleColor,
|
||||
"Expected hex color.",
|
||||
);
|
||||
}
|
||||
|
||||
const bandedColors = asFrequencyBandedColors(
|
||||
(frequencyDictionary as { bandedColors?: unknown }).bandedColors,
|
||||
);
|
||||
if (bandedColors !== undefined) {
|
||||
resolved.subtitleStyle.frequencyDictionary.bandedColors = bandedColors;
|
||||
} else if (
|
||||
(frequencyDictionary as { bandedColors?: unknown }).bandedColors !== undefined
|
||||
) {
|
||||
warn(
|
||||
"subtitleStyle.frequencyDictionary.bandedColors",
|
||||
(frequencyDictionary as { bandedColors?: unknown }).bandedColors,
|
||||
resolved.subtitleStyle.frequencyDictionary.bandedColors,
|
||||
"Expected an array of five hex colors.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isObject(src.ankiConnect)) {
|
||||
|
||||
Reference in New Issue
Block a user