feat(core): add Discord Rich Presence integration

Introduce optional Discord activity updates across config, runtime, tests, and docs so playback context appears in Discord without destabilizing app lifecycle. Tune default refresh cadence to reduce pause/resume lag during real sessions.
This commit is contained in:
2026-02-22 17:25:55 -08:00
parent edfe6640ac
commit f1dc418e2d
17 changed files with 238 additions and 346 deletions

View File

@@ -30,15 +30,6 @@ test('discordPresence fields are parsed and clamped', () => {
const { context } = createResolveContext({
discordPresence: {
enabled: true,
clientId: '123456789',
detailsTemplate: 'Watching {title}',
stateTemplate: 'Paused',
largeImageKey: 'subminer-logo',
largeImageText: 'SubMiner Runtime',
smallImageKey: 'pause',
smallImageText: 'Paused',
buttonLabel: 'Open Repo',
buttonUrl: 'https://github.com/sudacode/SubMiner',
updateIntervalMs: 500,
debounceMs: -100,
},
@@ -47,15 +38,6 @@ test('discordPresence fields are parsed and clamped', () => {
applyIntegrationConfig(context);
assert.equal(context.resolved.discordPresence.enabled, true);
assert.equal(context.resolved.discordPresence.clientId, '123456789');
assert.equal(context.resolved.discordPresence.detailsTemplate, 'Watching {title}');
assert.equal(context.resolved.discordPresence.stateTemplate, 'Paused');
assert.equal(context.resolved.discordPresence.largeImageKey, 'subminer-logo');
assert.equal(context.resolved.discordPresence.largeImageText, 'SubMiner Runtime');
assert.equal(context.resolved.discordPresence.smallImageKey, 'pause');
assert.equal(context.resolved.discordPresence.smallImageText, 'Paused');
assert.equal(context.resolved.discordPresence.buttonLabel, 'Open Repo');
assert.equal(context.resolved.discordPresence.buttonUrl, 'https://github.com/sudacode/SubMiner');
assert.equal(context.resolved.discordPresence.updateIntervalMs, 1000);
assert.equal(context.resolved.discordPresence.debounceMs, 0);
});
@@ -64,7 +46,6 @@ test('discordPresence invalid values warn and keep defaults', () => {
const { context, warnings } = createResolveContext({
discordPresence: {
enabled: 'true' as never,
clientId: 123 as never,
updateIntervalMs: 'fast' as never,
debounceMs: null as never,
},
@@ -73,13 +54,11 @@ test('discordPresence invalid values warn and keep defaults', () => {
applyIntegrationConfig(context);
assert.equal(context.resolved.discordPresence.enabled, false);
assert.equal(context.resolved.discordPresence.clientId, '');
assert.equal(context.resolved.discordPresence.updateIntervalMs, 15_000);
assert.equal(context.resolved.discordPresence.updateIntervalMs, 3_000);
assert.equal(context.resolved.discordPresence.debounceMs, 750);
const warnedPaths = warnings.map((warning) => warning.path);
assert.ok(warnedPaths.includes('discordPresence.enabled'));
assert.ok(warnedPaths.includes('discordPresence.clientId'));
assert.ok(warnedPaths.includes('discordPresence.updateIntervalMs'));
assert.ok(warnedPaths.includes('discordPresence.debounceMs'));
});