feat(overlay): add loading OSD spinner and queue notifications until ren

- Show mpv OSD spinner from start-file until subminer-overlay-loading-ready; force-shown for visible-overlay startup regardless of osd_messages setting
- Gate non-macOS overlay visibility on content-ready so first subtitle line is immediately hoverable and clickable
- Queue startup notifications in main process until overlay window finishes loading; upsert progress cards by id to avoid cold-start floods
- Defer background warmups until after overlay runtime init so queued notifications can deliver promptly
- Preserve character dictionary checking/building/importing/ready phases as distinct history entries; route building and importing to system notifications when notificationType is both
This commit is contained in:
2026-06-07 23:13:51 -07:00
parent d033884b09
commit 9d77907877
49 changed files with 1613 additions and 132 deletions
@@ -71,7 +71,7 @@ test('auto sync notifications send osd updates for progress phases', () => {
]);
});
test('auto sync notifications prefer overlay delivery for both when overlay is available', () => {
test('auto sync notifications send overlay and desktop delivery for both', () => {
const calls: string[] = [];
notifyCharacterDictionaryAutoSyncStatus(makeEvent('syncing', 'syncing'), {
@@ -83,7 +83,7 @@ test('auto sync notifications prefer overlay delivery for both when overlay is a
calls.push(`desktop:${title}:${options.body ?? ''}`),
showOverlayNotification: (payload) =>
calls.push(
`overlay:${payload.id}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
`overlay:${payload.id}:${payload.historyId}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
),
});
notifyCharacterDictionaryAutoSyncStatus(makeEvent('ready', 'ready'), {
@@ -95,13 +95,15 @@ test('auto sync notifications prefer overlay delivery for both when overlay is a
calls.push(`desktop:${title}:${options.body ?? ''}`),
showOverlayNotification: (payload) =>
calls.push(
`overlay:${payload.id}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
`overlay:${payload.id}:${payload.historyId}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
),
});
assert.deepEqual(calls, [
'overlay:character-dictionary-auto-sync:Character dictionary:syncing:pin',
'overlay:character-dictionary-auto-sync:Character dictionary:ready:auto',
'overlay:character-dictionary-auto-sync:character-dictionary-auto-sync-101291-syncing:Character dictionary:syncing:pin',
'desktop:SubMiner:syncing',
'overlay:character-dictionary-auto-sync:character-dictionary-auto-sync-101291-ready:Character dictionary:ready:auto',
'desktop:SubMiner:ready',
]);
});