diff --git a/src/notify.ts b/src/notify.ts index 81f99c4..69b6485 100644 --- a/src/notify.ts +++ b/src/notify.ts @@ -191,14 +191,16 @@ async function sendNative(ctx: Ctx, wants: { readonly desktop: boolean; readonly ctx.logger.debug('The bridge has no target for the enabled channels.'); return false; } - const lines = reportLines(report, ctx.settings.values.template, (e) => { warnTemplate(ctx, e); }); + const values = ctx.settings.values; + const lines = reportLines(report, values.template, (e) => { warnTemplate(ctx, e); }); const result = await ctx.native.notify({ title: lines[0] ?? `${report.joiner.name} joined`, content: lines.slice(1).join('\n'), - timeoutSecs: 8, + timeoutSecs: values.notifyTimeoutSecs, icon: 'security-high', sinks, - urgency: accentFor(report) === 'warn' ? 'critical' : 'normal', + // Never critical: KDE ignores the expiry for critical notifications and keeps them on screen. + urgency: 'normal', }); for (const failure of result.failed) { if (failure.sink !== 'bridge') ctx.logger.warn(`Bridge target ${failure.sink} failed: ${failure.error}`); diff --git a/src/settings.ts b/src/settings.ts index 426b872..9acd8a7 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -110,6 +110,15 @@ export const settings = { description: 'Through the VRCNext Bridge on any platform, or VRCNext’s tray toast on Windows.', default: true, }, + notifyTimeoutSecs: { + kind: 'number', + label: 'Seconds a desktop or VR notification stays', + description: 'Sent as the notification’s expiry. KDE keeps "critical" notifications until dismissed, so reports are never sent as critical.', + default: 30, + min: 1, + max: 60, + step: 1, + }, notifyVr: { kind: 'boolean', label: 'VR overlay notification',