From c31e580542d7ce3896989006456a729934e56ffe Mon Sep 17 00:00:00 2001 From: JohnsonKC201 Date: Thu, 18 Jun 2026 00:27:47 -0400 Subject: [PATCH] =?UTF-8?q?feat(jam):=20in-app=20Lobby=20Jam=20=E2=80=94?= =?UTF-8?q?=20synthesized=20lo-fi=20study=20music=20+=20guitar-playing=20c?= =?UTF-8?q?at?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cat plays an endless, improvising lo-fi guitar loop (Karplus-Strong, 100% synthesized — no audio files) and holds a little pixel guitar while it plays. - src/jam.js: the synth engine, reusing the overlay's shared AudioContext/master so it respects the Volume slider and mixes with meow/purr. - Settings: new "Lobby Jam" section (play toggle + Cozy/Dreamy/Upbeat mood) and a tray "Lobby Jam" toggle. Config persists { on, mood } and auto-resumes on launch. - renderer/effects: the cat holds + strums a procedurally-drawn guitar, bobs to the beat, and floats music notes (drawGuitar/drawNote); reduced-motion / low-power aware. - main: autoplay-policy switch so the music can resume at launch without a click. - tests: lobbyJam normalize test; correct a stale email-TLS assertion. --- eslint.config.js | 9 +- extras/lobby-jam.html | 338 +++++++++++++++++++++++++++++++++++++++ src/config.js | 5 + src/effects.js | 42 ++++- src/index.html | 1 + src/jam.js | 168 +++++++++++++++++++ src/main.js | 5 + src/renderer.js | 20 ++- src/settings-renderer.js | 6 + src/settings.html | 14 ++ tests/smoke.test.js | 10 +- 11 files changed, 613 insertions(+), 5 deletions(-) create mode 100644 extras/lobby-jam.html create mode 100644 src/jam.js diff --git a/eslint.config.js b/eslint.config.js index f028798..04d0adb 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,6 +18,7 @@ const sharedOverlay = { startPurr: 'readonly', stopPurr: 'readonly', playChirp: 'readonly', playMrrp: 'readonly', // effects.js provides these: drawThinkBubble: 'readonly', drawWorkBubble: 'readonly', drawDoneSpark: 'readonly', drawHeart: 'readonly', + drawGuitar: 'readonly', drawNote: 'readonly', }; const CONSUMER_OVERLAY = ['src/renderer.js', 'src/settings-renderer.js', 'src/cat-preview.js']; @@ -30,7 +31,7 @@ module.exports = [ { // Node / CommonJS: main process, workers, scripts, tests, configs, template.js files: ['**/*.js'], - ignores: [...CONSUMER_OVERLAY, 'src/cat-sprite.js', 'src/patterns.js', 'src/audio.js', 'src/effects.js'], + ignores: [...CONSUMER_OVERLAY, 'src/cat-sprite.js', 'src/patterns.js', 'src/audio.js', 'src/effects.js', 'src/jam.js'], languageOptions: { sourceType: 'commonjs', ecmaVersion: 2023, globals: { ...globals.node } }, }, { @@ -48,6 +49,12 @@ module.exports = [ files: ['src/effects.js'], languageOptions: { sourceType: 'script', ecmaVersion: 2023, globals: { ...globals.browser, ctx: 'readonly' } }, }, + { + // jam.js: classic overlay + + diff --git a/src/config.js b/src/config.js index c72a3e6..6e6c3f8 100644 --- a/src/config.js +++ b/src/config.js @@ -29,6 +29,7 @@ const DEFAULTS = { pinnedNote: '', // fixed message pinned above the cat's head ('' = off) notifyOn: true, // also pop a Windows toast for reminders/messages pomodoro: { on: false, focusMin: 25, breakMin: 5 }, // focus/break loops + floating pixel timer + lobbyJam: { on: false, mood: 'cozy' }, // synthesized lo-fi "study music" the cat plays (cozy/dreamy/upbeat) reminders: [], // [{ id, hhmm: 'HH:MM', message, recur, days, lastFired }] email: { on: false, host: '', port: 993, user: '', secure: true, intervalMin: 5 }, // IMAP unread alerts (app-password stored separately, encrypted) calendar: { on: false, icsUrl: '', leadMin: 10 }, // nudge before events from a secret .ics URL @@ -81,6 +82,10 @@ function normalize(cfg) { const p = (c.pomodoro && typeof c.pomodoro === 'object') ? c.pomodoro : {}; return { on: !!p.on, focusMin: clampInt(p.focusMin, 5, 120, 25), breakMin: clampInt(p.breakMin, 1, 60, 5) }; })(), + lobbyJam: (() => { + const lj = (c.lobbyJam && typeof c.lobbyJam === 'object') ? c.lobbyJam : {}; + return { on: !!lj.on, mood: ['cozy', 'dreamy', 'upbeat'].includes(lj.mood) ? lj.mood : 'cozy' }; + })(), email: (() => { const e = (c.email && typeof c.email === 'object') ? c.email : {}; const port = clampInt(e.port, 1, 65535, 993); diff --git a/src/effects.js b/src/effects.js index d8aff76..0979277 100644 --- a/src/effects.js +++ b/src/effects.js @@ -2,7 +2,7 @@ // spinner, "done!" burst, love heart). Classic + diff --git a/src/jam.js b/src/jam.js new file mode 100644 index 0000000..82b2a5a --- /dev/null +++ b/src/jam.js @@ -0,0 +1,168 @@ +// src/jam.js — "Lobby Jam": a synthesized, improvising lo-fi study-music loop the cat +// plays. 100% Web Audio (no asset files): Karplus-Strong plucked guitar over lazy jazzy +// voicings + soft bass + brushed percussion, tape-warmed. Classic overlay