feat(overlay): add primary subtitle bar visibility modes (#63)

- Cycle `v` through `hidden | visible | hover` instead of a boolean toggle
- Add `subtitleStyle.primaryDefaultMode` config with default `visible`
- Carry primary mode independently from secondary in hot-reload payload
- Add hover CSS: transparent until hovered, then fully visible
- Show primary-specific OSD text on each mode change
This commit is contained in:
2026-05-12 23:00:32 -07:00
committed by GitHub
parent 430373f010
commit e5c1135501
20 changed files with 221 additions and 13 deletions
+19
View File
@@ -147,6 +147,7 @@ export function applySubtitleDomainConfig(context: ResolveContext): void {
if (isObject(src.subtitleStyle)) {
const fallbackSubtitleStyleEnableJlpt = resolved.subtitleStyle.enableJlpt;
const fallbackSubtitleStylePrimaryDefaultMode = resolved.subtitleStyle.primaryDefaultMode;
const fallbackSubtitleStylePreserveLineBreaks = resolved.subtitleStyle.preserveLineBreaks;
const fallbackSubtitleStyleAutoPauseVideoOnHover = resolved.subtitleStyle.autoPauseVideoOnHover;
const fallbackSubtitleStyleAutoPauseVideoOnYomitanPopup =
@@ -190,6 +191,24 @@ export function applySubtitleDomainConfig(context: ResolveContext): void {
);
}
const primaryDefaultMode = (src.subtitleStyle as { primaryDefaultMode?: unknown })
.primaryDefaultMode;
if (
primaryDefaultMode === 'hidden' ||
primaryDefaultMode === 'visible' ||
primaryDefaultMode === 'hover'
) {
resolved.subtitleStyle.primaryDefaultMode = primaryDefaultMode;
} else if (primaryDefaultMode !== undefined) {
resolved.subtitleStyle.primaryDefaultMode = fallbackSubtitleStylePrimaryDefaultMode;
warn(
'subtitleStyle.primaryDefaultMode',
primaryDefaultMode,
resolved.subtitleStyle.primaryDefaultMode,
'Expected hidden, visible, or hover.',
);
}
const preserveLineBreaks = asBoolean(
(src.subtitleStyle as { preserveLineBreaks?: unknown }).preserveLineBreaks,
);
+25
View File
@@ -66,6 +66,31 @@ test('subtitleStyle autoPauseVideoOnYomitanPopup falls back on invalid value', (
);
});
test('subtitleStyle primaryDefaultMode accepts valid values and warns on invalid', () => {
const valid = createResolveContext({
subtitleStyle: {
primaryDefaultMode: 'hover',
},
});
applySubtitleDomainConfig(valid.context);
assert.equal(valid.context.resolved.subtitleStyle.primaryDefaultMode, 'hover');
const invalid = createResolveContext({
subtitleStyle: {
primaryDefaultMode: 'auto' as never,
},
});
applySubtitleDomainConfig(invalid.context);
assert.equal(invalid.context.resolved.subtitleStyle.primaryDefaultMode, 'visible');
assert.ok(
invalid.warnings.some(
(warning) =>
warning.path === 'subtitleStyle.primaryDefaultMode' &&
warning.message === 'Expected hidden, visible, or hover.',
),
);
});
test('subtitleStyle nameMatchEnabled falls back on invalid value', () => {
const { context, warnings } = createResolveContext({
subtitleStyle: {