Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ npm install -g badclaude
badclaude
```

## Platform support

- Windows: full support.
- macOS: full support after granting Accessibility permissions to the terminal or packaged app.
- Linux X11/Xorg: full whip mode when `xdotool` is installed.
- Linux Wayland: the app prefers X11/XWayland automatically when available so the overlay can render more reliably; global typing into native Wayland apps may still be unavailable by design.

### Linux notes

For full Linux automation on X11/Xorg, install `xdotool` first.

Examples:

```bash
# Debian/Ubuntu
sudo apt install xdotool

# Fedora
sudo dnf install xdotool

# Arch
sudo pacman -S xdotool
```

Wayland sessions can still run the app, but some desktops block the exact cross-app keyboard automation this toy relies on. On GNOME-like desktops, tray support may also depend on AppIndicator/StatusNotifier extensions.

On GNOME, badclaude also shows a small floating launcher window because the tray icon is often hidden entirely.

## Controls

- Click tray icon: spawn whip.
Expand All @@ -24,4 +52,4 @@ badclaude
- [x] Cease and desist letter from Anthropic
- [ ] 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
- [ ] Updated whip physics
9 changes: 8 additions & 1 deletion bin/badclaude.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ try {
}

const appPath = path.resolve(__dirname, '..');
const extraArgs = process.argv.slice(2);
const env = { ...process.env };

const child = spawn(electronBinary, [appPath], {
if (process.platform === 'linux' && env.WAYLAND_DISPLAY && env.DISPLAY && !env.ELECTRON_OZONE_PLATFORM_HINT) {
env.ELECTRON_OZONE_PLATFORM_HINT = 'x11';
}

const child = spawn(electronBinary, [appPath, ...extraArgs], {
detached: true,
env,
stdio: 'ignore',
windowsHide: true,
});
Expand Down
128 changes: 128 additions & 0 deletions launcher.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
color-scheme: dark;
--bg: rgba(17, 12, 10, 0.92);
--panel: rgba(44, 24, 16, 0.96);
--border: rgba(255, 214, 170, 0.24);
--text: #ffe9d7;
--muted: #c8ad96;
--accent: #ff8c42;
--accent-2: #ffd166;
}

* { box-sizing: border-box; }
html, body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: transparent;
font-family: "DejaVu Sans", "Noto Sans", sans-serif;
}

body {
display: grid;
place-items: center;
padding: 10px;
}

.card {
width: 100%;
height: 100%;
border-radius: 22px;
background:
radial-gradient(circle at top, rgba(255, 209, 102, 0.24), transparent 45%),
linear-gradient(160deg, var(--panel), var(--bg));
border: 1px solid var(--border);
box-shadow: 0 22px 50px rgba(0, 0, 0, 0.38);
display: grid;
grid-template-rows: 1fr auto;
padding: 12px;
}

button {
border: 0;
cursor: pointer;
color: var(--text);
font: inherit;
}

.launch {
border-radius: 18px;
background:
radial-gradient(circle at 30% 20%, rgba(255, 209, 102, 0.38), transparent 35%),
linear-gradient(180deg, rgba(255, 140, 66, 0.96), rgba(156, 60, 26, 0.96));
display: grid;
place-items: center;
min-height: 0;
transition: transform 120ms ease, filter 120ms ease;
}

.launch:hover { transform: translateY(-1px) scale(1.01); }
.launch:active { transform: scale(0.98); }

.emoji {
font-size: 40px;
line-height: 1;
filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.3));
}

.label {
margin-top: 6px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}

.footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 10px;
}

.hint {
font-size: 11px;
color: var(--muted);
padding-left: 2px;
}

.quit {
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
padding: 6px 10px;
font-size: 11px;
}
</style>
</head>
<body>
<div class="card">
<button class="launch" id="launch" title="Spawn whip">
<div>
<div class="emoji">!</div>
<div class="label">Whip</div>
</div>
</button>
<div class="footer">
<div class="hint">GNOME launcher</div>
<button class="quit" id="quit" title="Quit badclaude">Quit</button>
</div>
</div>
<script>
document.getElementById('launch').addEventListener('click', () => {
window.bridge.toggleOverlay();
});

document.getElementById('quit').addEventListener('click', () => {
window.bridge.quitApp();
});
</script>
</body>
</html>
Loading