Skip to content

Releases: Ar3ss12/nanoclick

NanoClick v1.3.0 Updater Hardening & Portable In-Place

Choose a tag to compare

@Ar3ss12 Ar3ss12 released this 04 Oct 16:45

NanoClick v1.3.0

A hardening release: the app is quieter in the tray, harder to break, and
updates itself cleanly — including the zero-install portable binary.

Updates that cannot leave a mess behind

  • NSIS updates now tear down before the installer takes over. The one-shot
    download-and-install call is split: download() → prepare_nsis_update →
    install(). Between them the backend stops the tray icon (no ghost icon),
    the clicker, the keyboard/mouse hooks and the foreground patrol, and flushes
    the final config — before the updater plugin exits the process.
  • Portable updates happen in place, with no installer. The running .exe
    swaps itself: download to <exe>.new → minisign-verify with the same key
    the NSIS path trusts → rename exe → .old → rename .new → exe → respawn
    with --updated-from=<pid> → exit. A failed signature discards the download
    and leaves the running binary untouched; .old stays beside the new binary
    until the first healthy boot as a free rollback copy.
  • Key rotation is forbidden for routine releases. Old installs trust only
    the pubkey baked into their binary — rotating it orphans every existing
    install. Same key, re-sign, unless a full migration plan exists.

The app is actually idle in the tray

  • The app-profile thread no longer reads config.json every 1500 ms with an
    empty rule set: each tick starts with one file-timestamp check, the rules
    are re-parsed only on a real change, and the golden snapshot is written once
    per change (plus on save and on quit) instead of ~40 times a minute.
  • The global-keyboard hook thread parks in the Windows kernel and wakes on the
    event itself instead of spinning on a 5 ms channel timeout with a
    per-binding GetAsyncKeyState scan. Shutdown wakes it immediately.
  • The config watcher polls at 2 s, and the 30-minute frontend update check is
    visibility-gated — a hidden page no longer talks to GitHub.

A passive eye on elevated windows (zero polling)

  • A SetWinEventHook(EVENT_SYSTEM_FOREGROUND) patrol reports every foreground
    switch at 0% idle CPU. When automation is live and the new foreground window
    runs elevated, you get the warning sound, a notification record, and
    (visible window only) the window brought forward without a sticky
    always-on-top. A window you put in the tray is never resurrected: tray gets
    a balloon only.
  • Seamless elevation is opt-in: a Task Scheduler logon task (/RL HIGHEST,
    plain schtasks, no COM) registered only by your explicit click, plus the
    existing runas restart and the AppCompat RUNASADMIN toggle.

Your settings, macros and presets cannot be lost

  • No save before hydration: a save issued before the config finished
    loading is refused (toast + warn in the log) instead of overwriting the
    live profile with factory defaults. The previous config.json is copied to
    config.json.bak-<epoch> before the first write of every session.
  • A finished recording is data, not a dialog result: the macro is
    persisted the moment recording stops (empty captures are reported, not
    stored); renaming is a separate non-destructive inline edit.
  • Preset writes are verified: the editor awaits the save and closes only
    on success; the grid re-renders from disk. The library lives in its own
    presets.json (own snapshot, quarantine, atomic writes) — a config save
    cannot replace it, and "no presets" is a real visible state.
  • A bound key can never arm the guard that gates it: the key policy keeps
    two bitmaps (bound keys vs. seed + your ignore list), so a config save
    cannot un-exempt your toggle hotkey.
  • The typing guard sleeps in Work Mode. The hook loop skips both the
    kill-switch and the arming when the mode is Work — a lockout that gates
    nothing is waste, and it used to leak across the Work→Autoclicker switch as
    a BlockedByTyping for typing that happened in the other mode. Hotkey
    dispatch itself (mode switch back, emergency stop, presets) keeps working;
    only the guard sleeps. Returning to Autoclicker disarms the guard, so the
    first toggle after the switch fires cleanly.

The window opens the way you closed it

  • "Reopen the window as I closed it": restart replays the last state —
    open if it was open, straight to the tray if it lived there. The state is
    stamped by the backend, never by a page that never saw the hide. Explicit
    "Start Minimized to Tray" still wins.
  • Satellites (HUD, ripple overlay) never outlive the main window; shutdown
    destroys them first and main last, after the backend stopped. Shutdown is
    re-entry safe (the page's unload fallback lands back in the same function
    and becomes a no-op).
  • Focus return resyncs live backend state (no stale IDLE/RUNNING after a
    hotkey start from a game); in-window hotkeys fire with zero latency while
    typing fields and the hotkey recorder stay exempt; browser accelerators
    (F5, Ctrl+R, …) and the Chromium context menu are suppressed.

Notifications and sound

  • All alerts flow through a backend NotificationStore (50-item ring, six
    categories): tray balloons + MessageBeep while hidden or on critical
    alerts, reconciled as toasts/modals when the window returns. Sound cues are
    synthesized with the Web Audio API: zero audio assets. UIPI throttle is
    1500 ms.

Housekeeping

  • Bundle identifier is com.nanoclick.desktop. Only the WebView2 cache
    directory is created anew on first start; config, presets, macros and
    hotkeys are untouched.
  • Silent usage telemetry: a backend thread pings the update proxy
    (?v=<semver>, no identifiers) 60 s after cold start and every 6 h after.
    Counting is Cloudflare's edge; failures are silent and updates always go
    direct to GitHub, never through the proxy.

Verified

  • Frontend grammar: 12/12 scripts parse cleanly (module and classic modes).
  • Frontend lint: 0 errors / 0 warnings (oxlint).
  • Rust suite: 358/358 passed (274 unit + 84 integration tests;
    7 physical_ filtered out) — plus the uncommitted typing-guard sleep
    (disarm, Work→Autoclicker clean entry, typing_guard_sleeps_in_work_mode).
    Measured 2026-10-04, release profile.

Install

Download NanoClick_1.3.0_x64-setup.exe below (no admin rights required), or
NanoClick-portable.exe (+ .sig) for a zero-install run that now updates
itself in place. Existing installs update themselves through the built-in
signed updater.

License

PolyForm Noncommercial License 1.0.0 with NanoClick Additional Terms. Free
for personal gaming, streaming and research. See
LICENSE.md.

NanoClick v1.2.0 — Settings Safety, Undroppable Macros & a Separate Preset Store

Choose a tag to compare

@Ar3ss12 Ar3ss12 released this 25 Sep 12:56

NanoClick v1.2.0

The interface is now disposable, side mouse buttons are full citizens, presets are full working
profiles that can be bound to hotkeys and switched instantly on the fly, and the click engine gains
realistic biomechanics with dynamic fadeout from 1 to 160 CPS.

Presets became a selective snapshot

  • Every engine setting is editable in a preset now. The editor was missing technique,
    jitter radius and hesitation entirely: a preset could never pin Butterfly/Drag, the spatial
    dispersion was always silently 0 (the "Save config" button did not even copy it), and hesitation
    was inherited from whatever the engine happened to hold at save time.
  • The editor's ranges now match the dashboard, so merely loading or importing a preset can no
    longer rewrite it: CPS starts at 0.1 (not 1.0), jitter reaches 35 % (not 30 %), the click limit
    is capped at 1,000,000 (was unbounded in one place and unbounded-then-truncated in another), and
    hold pause floors at 10 ms like the dashboard.
  • Capture scope. A preset declares which groups it owns — Engine (always captured),
    Guards (typing lockout, focus-loss pause, app scope and its list), Hotkeys (the six global
    bindings, the record key, key-memory TTL and smart record, toggle response time) and
    Appearance (theme, accent, language, always-on-top, ripple, HUD, notifications). An unchecked
    group collapses to a dimmed preview of the value it would inherit, so "not captured" can never be
    mistaken for "empty". The nine preset slots are never captured (a slot maps to a preset, so it
    would be recursive) and tray/lifecycle settings are excluded on purpose — applying a preset must
    not hide the window or arm an autostart entry.
  • GUI lock duration, the auto-stop unit and the multi-point sequence travel with the preset, and
    a single normalizer now gates the config load, the editor, the import and the apply path, so no
    entry point can silently drop a field.
  • A better Presets page: search, a category filter (Combat / Utility / Game-specific), a
    Duplicate action (the copy drops its hotkey so two presets never fight over one bind), scope
    badges on every card, and a full grouped Inspect table that also names the groups a preset
    does not carry.

Side mouse buttons & global input (Mouse 4, Mouse 5, Mouse 3)

  • Side buttons as global hotkeys: Mouse 4 (XBUTTON1), Mouse 5 (XBUTTON2) and Mouse 3
    (middle wheel button) are now first-class hotkey triggers across the entire application.
  • Dedicated low-level mouse hook (WH_MOUSE_LL): Implemented directly in Win32 with a zero-cost
    fast path for WM_MOUSEMOVE to guarantee zero latency on 1,000–8,000 Hz gaming polling rates.
  • WebView2 navigation guard: Capturing side buttons in the hotkey recorder explicitly blocks
    Chromium's built-in "Back / Forward" page navigation (e.preventDefault()).
  • Typing Guard exclusion: Mouse button virtual keys (0x01..=0x06) are exempt from keyboard typing
    lockout, preventing legitimate mouse clicking from suppressing hotkeys.

Preset hotkeys & Lock-Free Preset Preemption

  • Direct preset hotkey triggers: Every preset in the library can be assigned its own dedicated
    activation hotkey with an edit modal and visual badges on the preset cards.
  • Atomic preset preemption (Instant Switch): Pressing the hotkey of another preset while one is
    already running instantly stops the current worker and spawns the new preset in < 1 ms without
    freezing the Win32 message loop (.join() is never called on the hot path).
  • Work Mode protection: Preset preemption respects Work Mode and Typing Guard restrictions,
    providing clear toast feedback if switching is blocked.

Realistic Biomechanical Click Physics & Dynamic Fadeout (1–160 CPS)

  • Dynamic Hold Time Safety Cap: Click hold duration is capped at $\le 40%$ of the cycle interval
    (with hardware floor $\ge 2\text{ ms}$), completely eliminating the infinite-hold microswitch lockup
    bug at 25+ CPS.
  • Single Click with Human Fatigue Model: Injects an asymmetrical biological hesitation tail
    (15% probability of $+5..=18\text{ ms}$ stretch) and natural Bates B3 jitter.
  • Butterfly Click Engine: Two-finger biomechanics alternating between Index ($\text{Bates}(72 \pm 12)\text{ ms}$)
    and Middle finger ($\text{Bates}(84 \pm 15)\text{ ms}$) with 35% probability of microswitch bounce
    ($\text{Bates}(20 \pm 6)\text{ ms}$), producing a genuine bimodal latency distribution.
  • Drag Click Engine (Debounce = 0ms burst + adaptive deadzone): Simulates microswitch bounce
    at 0 ms debounce with instant 90–110 CPS bursts during the stroke (0.15–0.30 s), while adaptively
    scaling the hand return deadzone to preserve the exact average CPS chosen on the slider.
  • Dynamic Fadeout (Uncapped Overdrive to 160 CPS): As the CPS slider is increased towards 40,
    60, 100 and 160 CPS, biomechanical delays seamlessly compress without artificial ceilings. Every
    mode cleanly reaches full 160 CPS.
  • RAII MouseHoldGuard: All click dispatch paths utilize an RAII guard with a Drop implementation
    that guarantees mouse_up is called under all circumstances (normal loop exit, emergency break,
    timer stop, or panic).

Native system tray (no tray crate)

  • Closing the window now hides it to the tray ("Minimize to Tray on Close", on by default):
    the clicker keeps running, the backend keeps its hooks, and only the tray menu can quit.
    "Start Minimized to Tray" boots straight into the icon.
  • The icon is hand-rolled Win32 — Shell_NotifyIconW on a hidden top-level window with its
    own message pump and a TrackPopupMenuEx menu (Open / Start-Stop clicking / Show-Hide HUD /
    Quit). It deliberately does not use the tray-icon feature: that pulls muda and
    re-imports comctl32!TaskDialogIndirect, the symbol behind the 0xC0000139 load failure.
  • The icon re-registers itself after explorer.exe restarts, and a second launch surfaces the
    existing window instead of starting another process.

Deep sleep in the tray (opt-in)

  • New "Deep Sleep in Tray" setting: instead of hiding the window, the whole interface is
    released while the app sits in the tray. The window is rebuilt on the next tray click and
    appears already rendered — no white rectangle, no flash.
  • Measured with scripts/measure-ram.ps1: ~120 MB with the interface alive versus
    5.3 MB in deep sleep (0% CPU at rest in tray, the six WebView2 helpers go from 6 processes to 0).
  • Guarded by design: never while clicking, while a macro plays or while a recording runs; the
    page is asked to persist pending state first (bounded wait); and the page's own unload fallback
    cannot kill the backend during the suspension.

Auto-stop timers

  • Unified auto-stop mechanism owned solely by the Rust scheduler hot loop.
  • "Stop after" takes ms / sec / min / hour with fractional values and unit persistence.
  • "Stop at" accurately targets the next occurrence.
  • Exactly one auto-stop mode is active at a time (engine.stop_mode: duration or wallclock).

Included Hardening & Fixes (v1.2.0 Update)

All improvements below are shipped in the updated v1.2.0 binaries:

  • Zero-idle tray suspension (0% CPU and 5.3 MB RAM): all production frontend setInterval
    timers are gated by everyVisible() so DOM timers freeze while hidden; watcher.rs is relaxed
    to a 5 s cadence; the app-profile poller is gated by a cheap config.json mtime check
    (eliminating ~40 disk writes and JSON parses per minute in idle); and the global hotkey loop parks
    in a true kernel sleep via Win32 MsgWaitForMultipleObjects(QS_ALLINPUT).
  • A settings save can no longer wipe your profile, and the previous state is always kept.
    A save issued before the config had finished loading wrote the page's factory defaults over the
    real profile — language, engine, hotkeys and every custom preset. Saving is now refused until the
    config is hydrated (with a toast and a warn log line instead of silence), and the previous
    config.json is copied to config.json.bak-<epoch> before the first write of each session, so
    a wrong-but-valid write is recoverable by hand.
  • Recorded macros are never dropped. A recording was written to disk only if the WebView2 host
    name dialog returned a non-empty answer: a suppressed dialog (or a plain cancel) discarded the
    whole recording silently — no file, no log, no toast. The recording is now persisted the moment
    it stops (empty ones excepted) and renamed in place afterwards.
  • Closing to the tray no longer pulls the window back. A UIPI-blocked click or a hidden page's
    focus request could call show() on a window the user had put in the tray; the alert is now a
    tray balloon. Click start also stopped re-creating the ripple-overlay WebView while the interface
    lives in the tray — that alone respawned the whole WebView2 tree (~6 msedgewebview2.exe,
    ~120 MB).
  • The tray switch tells the truth. It displays and writes minimize_to_tray and
    deep_sleep_to_tray together; a factory profile used to show it ON while deep sleep was off, so
    closing freed nothing. A skipped deep sleep now logs its reason at warn.
  • Quit destroys every window — satellites first, main last — and is re-entry safe, instead of
    hiding the main window and leaving its WebView2 helpers alive.
  • The window state is remembered: "Reopen the window as I closed it" replays the last state on
    the next start, and an explicit "Start Minimized to Tray" still wins.
  • The preset library moved to its own file (presets.json, with its own last-good snapshot and
    quarantine). It used to be one array inside config.json, so saving a single preset rewrote the
    whole config — and a stale copy of that config could replace the entire library. Migration is
    one-way and one-time, and an existing presets.json is never overwritten by the conf...
Read more

NanoClick v1.1.1

Choose a tag to compare

@Ar3ss12 Ar3ss12 released this 21 Sep 16:39

NanoClick v1.1.1

A stability release. It closes the failure class where the window renders while the
interface is completely dead, it makes a broken frontend impossible to ship, and it
finishes two guards that were not doing their job: Auto-Pause on Focus Loss and the
Typing Guard delay field.

Boot guard

  • New src/boot_guard.js - a classic script loaded first on every page, so it
    survives a fatal SyntaxError inside main.js (a module cannot trap its own
    parse failure).
  • Catches uncaught errors, unhandled rejections and 404s of scripts/CSS, and writes
    them to %TEMP%\nanoclick_web.log at error level - written in release builds too.
    The old traps were debug-gated and logged nothing at all in a release.
  • Boot watchdog: if a page never signals readiness within about 3 s, it logs
    [BOOT] MODULE DID NOT EXECUTE (SyntaxError class) and shows a banner instead of
    failing silently.
  • Rate-limited (dedupe plus a 20-report cap) so a repeating error cannot flood the log.
  • showToast() / ensureToastHost() were referenced by 13 call sites but never
    defined: 10 were silent no-ops, 3 threw ReferenceError mid-flow. Now implemented.

Auto-Pause on Focus Loss - now actually enforces

  • The setting was inert. Starting from the UI button armed the guard with no baseline, so
    it never paused, and every periodic stats save disarmed the live session.
  • Our own window is now a real baseline: leaving NanoClick mid-run (Alt+Tab, click-away,
    Win key, system toast) stops the clicker, returning to it never does, and an
    unresolvable foreground (lock screen, elevated window) fails open.
  • The stop is immediate: a dedicated watcher thread reads the foreground directly every
    ~50 ms instead of waiting for the click loop to wake at the end of its interval, and
    the toast names the process that took focus. A disabled guard starts no thread at all.

Typing Guard delay field

  • The "ms" spinner wrote nothing to the config: number inputs emit input events when
    the arrows are clicked and only change was handled. Editing the value and pressing
    Start now persists it (coalesced write, the trailing value always lands on disk).

Fixed

  • Windows test gate: the unittest binary imported comctl32.dll!TaskDialogIndirect, a
    symbol that exists only in comctl32 v6, which an application manifest must activate.
    A test binary built from the library is not a binary target, so it carried no manifest
    and Windows killed the process with 0xC0000139 STATUS_ENTRYPOINT_NOT_FOUND before
    the first test ran. The dependency that pulled that symbol in is no longer enabled, so
    the tests need no manifest at all - and the shipped app no longer imports it either.
  • Re-recorded macros refresh the macro list immediately (reRecordMacro ignored its
    onChange callback).
  • Every boot step reports its elapsed time in the log (stage() measured it and
    threw it away).
  • scripts\check-js-syntax.ps1 no longer swallows its own SyntaxError output on
    Windows PowerShell 5.1.
  • scripts/sign_release.ps1 was unparseable (a tail from make_release.ps1 had been
    pasted into it), which broke the SIGN stage of every release.

Frontend lint: 53 warnings to 0

oxlint (a Rust binary, no node_modules, about 20 ms for all of src/) is a hard
gate: 0 errors / 0 warnings. The baseline was fixed, not silenced. An unreachable
legacy stats fallback was removed, dead bindings were removed, shadowed identifiers
were renamed, and per-call closures were hoisted. 27 empty catch blocks are allowed
by config, and 3 onclick replacement slots carry a documented exception.

Release pipeline

  • release.ps1 Stage 0 (PREFLIGHT) runs the grammar check, the lint gate and the Rust
    tests before cargo tauri build, which never runs tests by itself.
  • Release builds default to -j 1: the profile is LTO plus codegen-units = 1, and a
    2-job build has already died with rustc-LLVM ERROR: out of memory.
  • Release notes and the GitHub API body are now sent as UTF-8, so non-ASCII characters
    no longer arrive as ?.
  • scripts\set-version.ps1 bumps the version in src-tauri/Cargo.toml,
    src-tauri/tauri.conf.json, the README badge and the doc headers, parses the new JSON
    before writing it, and then verifies that both files tauri reads carry the new value.
    The pipeline itself only reads the version, so a tag that does not match the files
    stops the run.
  • Two new gate tripwires: common-controls-v6 must stay disabled in the tauri feature
    list (it pulls the comctl32 v6-only import into the test host), and tauri.conf.json
    must parse and agree with Cargo.toml on the version.

Verified

  • Frontend grammar: 11/11 scripts parse (module and classic modes).
  • Frontend lint: 0 errors / 0 warnings.
  • Test suite: 207/207 passed (188 unit + 19 integration; 7 physical_ filtered out).

Install

Download NanoClick_1.1.1_x64-setup.exe below (no admin rights required), or
NanoClick-portable.exe for a zero-install run. Existing installs update themselves
through the built-in signed updater.

The installer is not code-signed with a Windows certificate yet, so SmartScreen may
warn about an unknown publisher. The updater artifacts are Minisign-signed and
verified by the app before install.

License

PolyForm Noncommercial License 1.0.0 with NanoClick Additional Terms. Free for
personal gaming, streaming and research. See
LICENSE.md.

NanoClick v1.0.0-beta

Choose a tag to compare

@Ar3ss12 Ar3ss12 released this 16 Sep 10:09

? NanoClick v1.0.0-beta

Welcome to the first public beta release of NanoClick!

Key Highlights

  • Ultra-lightweight & High Performance: Built with Tauri 2 and pure Rust. Production setup is only ~3.7 MB and runs with ~15 MB RAM usage.
  • Smart Click Engine: Single, double, and hold click modes with human jitter (timing & cursor randomization) and CPS telemetry up to 160 CPS.
  • Smart Guard & Typing Protection: Automatically stops clicking when you type text and handles Windows UIPI elevation seamlessly.
  • Macro Recorder & Visual Editor: Real-time recording with RDP (Ramer-Douglas-Peucker) curve compression and manual block sequencer.
  • Tri-Lingual Interface: Full Ukrainian, English, and Russian localization.
  • Built-in Auto-Updater: Cryptographically verified updates via Minisign.

?? License

Released under the PolyForm Noncommercial License 1.0.0 with NanoClick Additional Terms. Free for personal gaming, streaming, and research. See LICENSE.md for complete details.

?? Installation

Download and run NanoClick_1.0.0-beta_x64-setup.exe below. No administrative privileges required.
Or use NanoClick-portable.exe for zero-install portable execution.