From 5ff97f43a1788c2720d4f34a2080be49f001f9c0 Mon Sep 17 00:00:00 2001 From: Akbar Jorayev Date: Tue, 7 Apr 2026 21:10:57 +0500 Subject: [PATCH] feat: add whip counter and visual crack effects --- README.md | 5 ++++- main.js | 19 ++++++++++++++----- overlay.html | 36 ++++++++++++++++++++++++++++++++++++ package-lock.json | 32 ++++++++++++++++++++------------ package.json | 2 +- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e7756af..79bab03 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,14 @@ badclaude - Click: drop whip. - Whip him 😩💢 - It sends an interrupt (Ctrl-C) and one of 5 encouraging messages! +- Shows a visual "CRACK!" text at the tip when you whip hard enough! +- Tracks your total whips in the tray menu. ## Roadmap - [x] Initial release! 🥳 - [x] Cease and desist letter from Anthropic +- [x] Logs of how many times you whipped claude +- [x] Visual feedback on "crack" - [ ] Crypto miner -- [ ] Logs of how many times you whipped claude so when the robots come we can order people nicely for them - [ ] Updated whip physics \ No newline at end of file diff --git a/main.js b/main.js index 526d9ec..bd0febc 100644 --- a/main.js +++ b/main.js @@ -21,6 +21,7 @@ if (process.platform === 'win32') { let tray, overlay; let overlayReady = false; let spawnQueued = false; +let whipCount = 0; const VK_CONTROL = 0x11; const VK_RETURN = 0x0D; @@ -29,6 +30,16 @@ const VK_MENU = 0x12; // Alt const VK_TAB = 0x09; const KEYUP = 0x0002; +function updateTrayMenu() { + if (!tray) return; + const contextMenu = Menu.buildFromTemplate([ + { label: `Total Whips: ${whipCount}`, enabled: false }, + { type: 'separator' }, + { label: 'Quit', click: () => app.quit() }, + ]); + tray.setContextMenu(contextMenu); +} + /** One Alt+Tab / Cmd+Tab so focus returns to the previously active app after tray click. */ function refocusPreviousApp() { const delayMs = 80; @@ -166,6 +177,8 @@ function toggleOverlay() { // ── IPC ───────────────────────────────────────────────────────────────────── ipcMain.on('whip-crack', () => { + whipCount++; + updateTrayMenu(); try { sendMacro(); } catch (err) { @@ -243,11 +256,7 @@ function sendMacroMac(text) { app.whenReady().then(async () => { tray = new Tray(await getTrayIcon()); tray.setToolTip('Bad Claude – click for whip'); - tray.setContextMenu( - Menu.buildFromTemplate([ - { label: 'Quit', click: () => app.quit() }, - ]) - ); + updateTrayMenu(); tray.on('click', toggleOverlay); }); diff --git a/overlay.html b/overlay.html index 2354b86..1ef6b2a 100644 --- a/overlay.html +++ b/overlay.html @@ -84,6 +84,7 @@ let whipSpawnTime = 0; let handleAngle = P.baseTargetAngle; let handleAngVel = 0; +let cracks = []; const WHIP_CRACK_SOUNDS = ['sounds/A.mp3', 'sounds/B.mp3', 'sounds/C.mp3', 'sounds/D.mp3', 'sounds/E.mp3']; @@ -97,6 +98,7 @@ dropping = false; lastCrackTime = 0; whipSpawnTime = Date.now(); + cracks = []; const pts = []; for (let i = 0; i < P.segments; i++) { const t = i / (P.segments - 1); @@ -363,9 +365,22 @@ lastCrackTime = now; playCrackSound(); window.bridge.whipCrack(); + + // Add visual crack effect + cracks.push({ + x: tip.x, + y: tip.y, + time: now, + text: ['CRACK!', 'POW!', 'SNAP!', 'WHIP!'][Math.floor(Math.random() * 4)], + rotation: (Math.random() - 0.5) * 0.5 + }); } } + // Update cracks (fade out) + const now = Date.now(); + cracks = cracks.filter(c => now - c.time < 600); + // If dropping, check if everything fell off screen if (dropping && whip.every(p => p.y > H + 60)) { whip = null; @@ -425,6 +440,27 @@ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2); ctx.stroke(); } + + // Draw cracks + const now = Date.now(); + for (const c of cracks) { + const age = now - c.time; + const alpha = 1 - age / 600; + const scale = 1 + age / 300; + ctx.save(); + ctx.translate(c.x, c.y); + ctx.rotate(c.rotation); + ctx.scale(scale, scale); + ctx.font = 'bold 24px sans-serif'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`; + ctx.strokeStyle = `rgba(0, 0, 0, ${alpha})`; + ctx.lineWidth = 4; + ctx.strokeText(c.text, 0, 0); + ctx.fillText(c.text, 0, 0); + ctx.restore(); + } } // ── Main loop ─────────────────────────────────────────────────────────────── diff --git a/package-lock.json b/package-lock.json index 5816cb2..65e382e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,12 +7,20 @@ "": { "name": "badclaude", "version": "1.0.2", + "license": "MIT", + "os": [ + "darwin", + "win32" + ], "dependencies": { - "electron": "^33.0.0", + "electron": "^41.1.1", "koffi": "^2.9.0" }, "bin": { "badclaude": "bin/badclaude.js" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@electron/get": { @@ -88,12 +96,12 @@ } }, "node_modules/@types/node": { - "version": "20.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", - "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/responselike": { @@ -268,14 +276,14 @@ "optional": true }, "node_modules/electron": { - "version": "33.4.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz", - "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", + "version": "41.1.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-41.1.1.tgz", + "integrity": "sha512-8bgvDhBjli+3Z2YCKgzzoBPh6391pr7Xv2h/tTJG4ETgvPvUxZomObbZLs31mUzYb6VrlcDDd9cyWyNKtPm3tA==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -781,9 +789,9 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, "node_modules/universalify": { diff --git a/package.json b/package.json index 463b497..2694624 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "pack": "npm pack" }, "dependencies": { - "electron": "^33.0.0", + "electron": "^41.1.1", "koffi": "^2.9.0" } }