Loading...
Loading...
An accuracy-focused Windows auto-clicker with advanced features including CPS control, keyboard automation, position clicking, and performance optimization built with TypeScript and Tauri.
npx skill4agent add aradotso/devtools-skills blur-autoclicker-automationSkill by ara.so — Devtools Skills collection.
# Download from https://github.com/Blur009/Blur-AutoClicker/releases/latest
# Default installation location
%localappdata%\BlurAutoClicker\BlurAutoClicker.exe
# Config and stats location
%appdata%\BlurAutoClicker# Clone the repository
git clone https://github.com/Blur009/Blur-AutoClicker.git
cd Blur-AutoClicker
# Install dependencies
npm install
# Set up Rust toolchain
rustup default stable-x86_64-pc-windows-msvc# Run in development mode
npm run dev
# Build release bundle
npm run build
# Lint code
npm run lint
# Build frontend only
npm run frontend:build
# Run Rust tests
cargo test --manifest-path src-tauri/Cargo.toml
# Check Rust code
cargo check --manifest-path src-tauri/Cargo.tomlsrc-tauri/target/release/bundle/nsis/Blur-AutoClicker/
├── src/ # TypeScript frontend (web UI)
├── src-tauri/ # Rust backend (Tauri)
│ ├── src/ # Rust source code
│ ├── Cargo.toml # Rust dependencies
│ └── tauri.conf.json # Tauri configuration
├── public/ # Static assets
└── package.json # Node.js dependencies// Example configuration for mouse button selection
interface MouseConfig {
button: 'left' | 'right' | 'middle';
mode: 'hold' | 'toggle';
cps: number; // Clicks per second (max 500)
}
const config: MouseConfig = {
button: 'left',
mode: 'toggle',
cps: 50
};interface KeyConfig {
key: string;
uppercase: boolean;
mode: 'hold' | 'toggle';
pressesPerSecond: number;
}
const keyConfig: KeyConfig = {
key: 'a',
uppercase: false,
mode: 'hold',
pressesPerSecond: 10
};interface TimingConfig {
clickDuration: number; // milliseconds
interval: number; // milliseconds between clicks
}
// For 50 CPS with 50% duty cycle
const timing: TimingConfig = {
clickDuration: 10, // 10ms press
interval: 20 // 20ms total (1000ms / 50 CPS)
};interface SpeedRangeConfig {
enabled: boolean;
minCps: number;
maxCps: number;
}
const speedRange: SpeedRangeConfig = {
enabled: true,
minCps: 40,
maxCps: 60
};interface PositionConfig {
enabled: boolean;
x: number;
y: number;
moveBeforeClick: boolean;
}
const positionClick: PositionConfig = {
enabled: true,
x: 1920,
y: 1080,
moveBeforeClick: true
};interface LimitsConfig {
clickLimit?: number; // Stop after X clicks
timeLimit?: number; // Stop after X seconds
}
const limits: LimitsConfig = {
clickLimit: 1000,
timeLimit: 60 // 60 seconds
};interface EdgeStoppingConfig {
enabled: boolean;
cornerThreshold: number; // pixels from corner
edgeThreshold: number; // pixels from edge
}
const edgeStopping: EdgeStoppingConfig = {
enabled: true,
cornerThreshold: 50,
edgeThreshold: 10
};import { invoke } from '@tauri-apps/api/tauri';
// Start clicking
async function startClicking(config: ClickConfig): Promise<void> {
await invoke('start_clicking', { config });
}
// Stop clicking
async function stopClicking(): Promise<void> {
await invoke('stop_clicking');
}
// Get click stats
async function getStats(): Promise<ClickStats> {
return await invoke('get_stats');
}
interface ClickStats {
totalClicks: number;
currentCps: number;
averageCps: number;
uptime: number;
}// src-tauri/src/main.rs
use tauri::State;
use std::sync::Mutex;
struct ClickerState {
is_active: bool,
total_clicks: u64,
}
#[tauri::command]
fn start_clicking(state: State<Mutex<ClickerState>>) -> Result<(), String> {
let mut clicker = state.lock().unwrap();
clicker.is_active = true;
Ok(())
}
#[tauri::command]
fn stop_clicking(state: State<Mutex<ClickerState>>) -> Result<(), String> {
let mut clicker = state.lock().unwrap();
clicker.is_active = false;
Ok(())
}
#[tauri::command]
fn get_stats(state: State<Mutex<ClickerState>>) -> Result<u64, String> {
let clicker = state.lock().unwrap();
Ok(clicker.total_clicks)
}import { appDataDir } from '@tauri-apps/api/path';
import { writeTextFile, readTextFile } from '@tauri-apps/api/fs';
async function saveConfig(config: AppConfig): Promise<void> {
const appData = await appDataDir();
const configPath = `${appData}/config.json`;
await writeTextFile(configPath, JSON.stringify(config, null, 2));
}
async function loadConfig(): Promise<AppConfig | null> {
try {
const appData = await appDataDir();
const configPath = `${appData}/config.json`;
const content = await readTextFile(configPath);
return JSON.parse(content);
} catch (error) {
console.error('Failed to load config:', error);
return null;
}
}
interface AppConfig {
mouseButton: 'left' | 'right' | 'middle';
mode: 'hold' | 'toggle';
cps: number;
hotkey: string;
advanced: AdvancedConfig;
}
interface AdvancedConfig {
dutyCycle: number;
speedRange?: SpeedRangeConfig;
positionClick?: PositionConfig;
limits?: LimitsConfig;
edgeStopping?: EdgeStoppingConfig;
}import { register, unregister } from '@tauri-apps/api/globalShortcut';
async function setupHotkeys(hotkey: string): Promise<void> {
// Unregister previous hotkey
await unregister(hotkey).catch(() => {});
// Register new hotkey
await register(hotkey, async () => {
// Toggle clicking state
const isActive = await invoke('is_clicking_active');
if (isActive) {
await invoke('stop_clicking');
} else {
await invoke('start_clicking');
}
});
}
// Common hotkey combinations
const hotkeyExamples = [
'F6',
'F7',
'F8',
'Ctrl+Shift+A',
'Alt+C'
];// src-tauri/src/timer.rs
use std::time::{Duration, Instant};
use std::thread;
pub struct PrecisionTimer {
interval: Duration,
last_tick: Instant,
}
impl PrecisionTimer {
pub fn new(cps: u32) -> Self {
let interval = Duration::from_micros(1_000_000 / cps as u64);
Self {
interval,
last_tick: Instant::now(),
}
}
pub fn wait_for_next_tick(&mut self) {
let elapsed = self.last_tick.elapsed();
if elapsed < self.interval {
thread::sleep(self.interval - elapsed);
}
self.last_tick = Instant::now();
}
}
// Usage
let mut timer = PrecisionTimer::new(50); // 50 CPS
loop {
// Perform click
send_click();
timer.wait_for_next_tick();
}// Clear click history periodically
let clickHistory: number[] = [];
const MAX_HISTORY_SIZE = 1000;
function recordClick(timestamp: number): void {
clickHistory.push(timestamp);
// Keep only recent clicks
if (clickHistory.length > MAX_HISTORY_SIZE) {
clickHistory = clickHistory.slice(-MAX_HISTORY_SIZE);
}
}
function calculateCps(): number {
const now = Date.now();
const recentClicks = clickHistory.filter(t => now - t < 1000);
return recentClicks.length;
}# 1. Update version in package.json and Cargo.toml
# 2. Clean previous builds
Remove-Item -Recurse -Force src-tauri/target/release/bundle -ErrorAction SilentlyContinue
# 3. Build release
npm run build
# 4. Installer location
# src-tauri/target/release/bundle/nsis/Blur-AutoClicker_X.X.X_x64-setup.exe
# 5. Test installer
Start-Process "src-tauri\target\release\bundle\nsis\*.exe"# Note: Windows Authenticode signing is separate from Tauri updater signing
# See docs/windows-release-trust.md for complete signing instructions
# Environment variables needed for signing
# WINDOWS_CERTIFICATE (base64 certificate)
# WINDOWS_CERTIFICATE_PASSWORD (certificate password)// src/lib/modes/customMode.ts
interface CustomModeConfig {
pattern: number[]; // Array of delays in milliseconds
repeat: boolean;
}
class CustomClickMode {
private config: CustomModeConfig;
private patternIndex: number = 0;
constructor(config: CustomModeConfig) {
this.config = config;
}
getNextDelay(): number {
const delay = this.config.pattern[this.patternIndex];
this.patternIndex = (this.patternIndex + 1) % this.config.pattern.length;
return delay;
}
async executePattern(): Promise<void> {
do {
for (const delay of this.config.pattern) {
await invoke('send_click');
await new Promise(resolve => setTimeout(resolve, delay));
}
} while (this.config.repeat);
}
}
// Usage: Click with pattern [100ms, 200ms, 100ms, 500ms]
const customMode = new CustomClickMode({
pattern: [100, 200, 100, 500],
repeat: true
});// src/lib/stats.ts
interface ClickStatistics {
totalClicks: number;
sessionClicks: number;
averageCps: number;
peakCps: number;
uptime: number; // seconds
startTime: number;
}
class StatsTracker {
private stats: ClickStatistics;
private clickTimestamps: number[] = [];
constructor() {
this.stats = {
totalClicks: 0,
sessionClicks: 0,
averageCps: 0,
peakCps: 0,
uptime: 0,
startTime: Date.now()
};
}
recordClick(): void {
const now = Date.now();
this.stats.totalClicks++;
this.stats.sessionClicks++;
this.clickTimestamps.push(now);
// Keep only last second of clicks
const oneSecondAgo = now - 1000;
this.clickTimestamps = this.clickTimestamps.filter(t => t > oneSecondAgo);
const currentCps = this.clickTimestamps.length;
if (currentCps > this.stats.peakCps) {
this.stats.peakCps = currentCps;
}
}
getStats(): ClickStatistics {
this.stats.uptime = Math.floor((Date.now() - this.stats.startTime) / 1000);
this.stats.averageCps = this.stats.uptime > 0
? this.stats.sessionClicks / this.stats.uptime
: 0;
return { ...this.stats };
}
async save(): Promise<void> {
await saveConfig({ stats: this.stats });
}
}docs/windows-release-trust.md// Enable debug logging
const DEBUG = true;
async function debugClickAccuracy(targetCps: number, duration: number): Promise<void> {
const clicks: number[] = [];
const startTime = Date.now();
while (Date.now() - startTime < duration) {
const clickTime = Date.now();
await invoke('send_click');
clicks.push(clickTime);
if (DEBUG) {
// Calculate actual CPS every second
const recentClicks = clicks.filter(t => clickTime - t < 1000);
console.log(`Target: ${targetCps} CPS, Actual: ${recentClicks.length} CPS`);
}
}
// Final accuracy report
const totalTime = (Date.now() - startTime) / 1000;
const actualCps = clicks.length / totalTime;
const accuracy = (actualCps / targetCps) * 100;
console.log(`Accuracy: ${accuracy.toFixed(2)}%`);
}// Monitor memory usage
if (performance.memory) {
const usedMemory = performance.memory.usedJSHeapSize / 1024 / 1024;
console.log(`Memory usage: ${usedMemory.toFixed(2)} MB`);
if (usedMemory > 80) {
// Clear unnecessary data
clickHistory = clickHistory.slice(-100);
// Force garbage collection (if available)
if (global.gc) global.gc();
}
}# Clean install
Remove-Item -Recurse -Force node_modules
Remove-Item package-lock.json
npm install
# Clean Rust build
cargo clean --manifest-path src-tauri/Cargo.toml
# Update Rust toolchain
rustup update stable-x86_64-pc-windows-msvc
# Rebuild
npm run build// Ensure proper Tauri API imports
import { invoke } from '@tauri-apps/api/tauri';
// Check Tauri configuration
// src-tauri/tauri.conf.json
{
"tauri": {
"allowlist": {
"all": false,
"fs": {
"all": false,
"readFile": true,
"writeFile": true
},
"globalShortcut": {
"all": true
}
}
}
}// Example test for click accuracy
describe('Click Accuracy', () => {
it('should click at specified CPS within 5% tolerance', async () => {
const targetCps = 50;
const duration = 5000; // 5 seconds
const tolerance = 0.05; // 5%
const clicks = await testClickAccuracy(targetCps, duration);
const actualCps = clicks.length / (duration / 1000);
const lowerBound = targetCps * (1 - tolerance);
const upperBound = targetCps * (1 + tolerance);
expect(actualCps).toBeGreaterThanOrEqual(lowerBound);
expect(actualCps).toBeLessThanOrEqual(upperBound);
});
});