|
| 1 | +# Misclick <img src="browser_extension/static/misclick_with_mouse_transparent.png" alt="Misclick Logo" width="80" align="right"/> |
| 2 | + |
| 3 | +<p align="center"> |
| 4 | + <img src="mobile_page/static/mouse_pointer.png" alt="Mouse Pointer"/> |
| 5 | +</p> |
| 6 | +<p align="center">"<b>Wrong mouse, for a reason</b>"</p> |
| 7 | +<p align="center"> |
| 8 | + <!-- Stars --> |
| 9 | + <img src="https://img.shields.io/github/stars/SOORAJTS2001/daring-daffodils?style=for-the-badge" alt="GitHub Stars"/> |
| 10 | + |
| 11 | + <!-- Forks --> |
| 12 | + <img src="https://img.shields.io/github/forks/SOORAJTS2001/daring-daffodils?style=for-the-badge" alt="GitHub Forks"/> |
| 13 | + |
| 14 | + <!-- Issues --> |
| 15 | + <img src="https://img.shields.io/github/issues/SOORAJTS2001/daring-daffodils?style=for-the-badge" alt="GitHub Issues"/> |
| 16 | + |
| 17 | + <!-- License --> |
| 18 | + <img src="https://img.shields.io/github/license/SOORAJTS2001/daring-daffodils?style=for-the-badge" alt="GitHub License"/> |
| 19 | +</p> |
| 20 | +Sure, you have a perfectly good keyboard and mouse… but why not run Python in your browser with a mobile? |
| 21 | +What about doing a scroll, drag and maybe a click or that's all ? |
| 22 | + |
| 23 | +That is Misclick - A way to control your mouse, **but in opposite way** |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## 📑 Table of Contents |
| 28 | + |
| 29 | +- [What can it do](#what-can-it-do) |
| 30 | +- [How to install](#how-to-install) |
| 31 | +- [How to use](#how-to-use) |
| 32 | +- [How it is working](#how-it-is-working) |
| 33 | +- [Limitations](#limitations) |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## What can it do |
| 38 | + |
| 39 | +- Oh, just casually pair your phone with your browser over WebSocket — because Bluetooth and USB are *too mainstream*. |
| 40 | +- Control your browser’s mouse with your phone, but in reverse. Yes, swipe left to go right. Because logic is overrated. |
| 41 | + - What’s on the menu? |
| 42 | + - Swipe left, swipe right — like Tinder, but for your cursor. |
| 43 | + - Swipe up or swipe down. |
| 44 | + - Click links and sections — basically what your mouse already does, but now slower and more annoying. |
| 45 | + - Scroll up, scroll down — congratulations, you’ve invented scrolling. |
| 46 | +- Feeling idle? Don’t worry, the mouse entertains itself with *modes*: |
| 47 | + - **Wander Mode** – your cursor takes itself on a joyride. You’re just a spectator. |
| 48 | + - **Rage Mode** – because why not double the speed and lose control even faster? |
| 49 | + - **Shadow Mode** – cursor goes invisible. Perfect for when you *really* want to rage-quit. |
| 50 | + - Sometimes you get all of them combined, so best of luck. |
| 51 | + - Bonus: it randomly clicks on stuff, so enjoy your surprise shopping carts and unexpected **easter eggs**. |
| 52 | + - If you get redirected to another page, don’t worry — the chaos restarts automatically. |
| 53 | +- **Drag text from your browser and send it to your phone. Groundbreaking. Nobel-worthy.** |
| 54 | +- Install it as a browser extension, and enjoy the privilege of also opening a webpage on your phone. Wow. |
| 55 | +- Cross-platform. Yes, it works everywhere, so nobody is safe. |
| 56 | +- Runs locally, no hosting, no leaks — except your sanity. |
| 57 | +- No accounts, no personal info — because who would *willingly* sign up for this anyway? |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## How to install |
| 62 | + |
| 63 | +### Prerequisites |
| 64 | + |
| 65 | +- Python 3.12+ |
| 66 | +- A modern web browser (Chrome recommended) |
| 67 | + |
| 68 | +### Steps |
| 69 | + |
| 70 | +1. Clone the repository: |
| 71 | + ```bash |
| 72 | + git clone https://github.com/SOORAJTS2001/daring-daffodils |
| 73 | + cd daring-daffodils |
| 74 | +2. Installing Extension |
| 75 | + 1. Open [chrome://extensions/](chrome://extensions/) and enable **Developer mode** as shown below: |
| 76 | + |
| 77 | +  |
| 78 | + 2. Click Load unpacked button and select |
| 79 | + the [browser_extension](https://github.com/SOORAJTS2001/daring-daffodils/tree/main/browser_extension) folder |
| 80 | + inside |
| 81 | + the **cloned repo** |
| 82 | +  |
| 83 | +3. Starting up server — **Make sure your PC/Laptop and phone are connected via the same WiFi** |
| 84 | + |
| 85 | + 1. Option 1: Using [Makefile](https://en.wikipedia.org/wiki/Make_(software)) |
| 86 | + 1. Just run the command: |
| 87 | + - Linux/MacOS |
| 88 | + ```bash |
| 89 | + make |
| 90 | + ``` |
| 91 | + - Windows |
| 92 | + - By default, Windows does not include GNU Make. Install via [this](https://www.msys2.org/) |
| 93 | + ```bash |
| 94 | + make |
| 95 | + ``` |
| 96 | + inside the root directory, where the `Makefile` is located. |
| 97 | + 2. Option 2: Manual setup |
| 98 | + 1. Create your environment: |
| 99 | + ```bash |
| 100 | + python3 -m venv .env |
| 101 | + ``` |
| 102 | + 2. Activate it: |
| 103 | + - Linux/MacOS |
| 104 | + ```bash |
| 105 | + source .env/bin/activate |
| 106 | + ``` |
| 107 | + - Windows |
| 108 | + ```bash |
| 109 | + .env\Scripts\Activate.ps1 |
| 110 | + ``` |
| 111 | + 3. Install Poetry: |
| 112 | + ```bash |
| 113 | + pip install poetry |
| 114 | + ``` |
| 115 | + 4. Install dependencies: |
| 116 | + ```bash |
| 117 | + poetry install |
| 118 | + ``` |
| 119 | + 5. Start the server |
| 120 | + ```bash |
| 121 | + python3 app.py |
| 122 | + ``` |
| 123 | + 3. After the server starts, it will show you a QR |
| 124 | + code. [Open that with your phone](https://www.android.com/articles/how-do-you-scan-qr-codes-on-android/) |
| 125 | + <p align="center"> |
| 126 | + <img src="documentation/server_qr_code.png" alt="Mouse Pointer"/> |
| 127 | + </p> |
| 128 | + |
| 129 | +## How to use |
| 130 | + |
| 131 | +<table align="center"> |
| 132 | + <tr> |
| 133 | + <td align="center"> |
| 134 | + <img src="documentation/mobile_page.png" alt="Mobile Page" width="300"/><br/> |
| 135 | + <b>Mobile page</b> |
| 136 | + </td> |
| 137 | + <td align="center"> |
| 138 | + <img src="documentation/activating_extension.gif" |
| 139 | + alt="Activating Browser Extension" width="600"/><br/> |
| 140 | + <b>Browser extension</b> |
| 141 | + </td> |
| 142 | + </tr> |
| 143 | + <tr> |
| 144 | + <td align="center"> |
| 145 | + <img src="documentation/misclick_intro.gif" alt="Browser extension - Basic movement" width="300"/><br/> |
| 146 | + <b>Browser extension - Basic movement</b> |
| 147 | + </td> |
| 148 | + <td align="center"> |
| 149 | + <img src="documentation/misclick_mode.gif" alt="Browser extension - Mode Activation" width="300"/><br/> |
| 150 | + <b>Browser extension - Mode Activation</b> |
| 151 | + </td> |
| 152 | + </tr> |
| 153 | + <tr> |
| 154 | + <td align="center"> |
| 155 | + <img src="documentation/browser_selection.png" alt="Browser extension - Selection" width="300"/><br/> |
| 156 | + <b>Browser extension - Selection</b> |
| 157 | + </td> |
| 158 | + <td align="center"> |
| 159 | + <img src="documentation/mobile_send.png" |
| 160 | + alt="Received by phone" width="300" height="500"/><br/> |
| 161 | + <b>Text Received by phone</b> |
| 162 | + </td> |
| 163 | + </tr> |
| 164 | +</table> |
| 165 | +<p align="center"> |
| 166 | + <img src="documentation/utils_not_found_error.png" alt="Mouse Pointer"/> |
| 167 | +</p> |
| 168 | + |
| 169 | +**⚠️ If you see an error like above** |
| 170 | +or the mouse is not showing up: |
| 171 | + |
| 172 | +- Hard refresh the page |
| 173 | +- Restart the server |
| 174 | + |
| 175 | +## How it is working |
| 176 | + |
| 177 | +### Architecture Diagram |
| 178 | + |
| 179 | +<p align="center"> |
| 180 | + <img src="documentation/architecture.png" alt="Mouse Pointer"/> |
| 181 | + </p> |
| 182 | + |
| 183 | +### Tech used |
| 184 | + |
| 185 | +- Pyscript/pyodide for rendering UI |
| 186 | +- `fastapi` for serving static pages and as a websocket server |
| 187 | +- `qrcode` library for generating url for mobile page |
| 188 | +- `pyodide` wrapper js to setup runtime for python in browser extension |
| 189 | + |
| 190 | +### Working |
| 191 | + |
| 192 | +<b>The backend is bind to the port `8000`</b> |
| 193 | + |
| 194 | +#### Mobile page |
| 195 | + |
| 196 | +- Once server is up, the mobile page url is shown in the terminal |
| 197 | +- On opening the mobile page, the websocket connection between the server and the frontend is established via pyscript |
| 198 | +- Inside the `mobile_page.py` it calls the Javascript native API's using pyodide/pyscript and manipulates the DOM |
| 199 | +- We have eventListeners in js, which is used for listen and trigger to user events like drag, scroll and click |
| 200 | +- We are having a proxy between the js and python for object and function transfer as |
| 201 | + explained [here](https://jeff.glass/post/pyscript-why-create-proxy/) |
| 202 | +- For every js `touchstart` event we would record it, until it ends with `touchend` and send the <b>change ratio</b> |
| 203 | + rather than coordinates via |
| 204 | + websocket to the browser |
| 205 | +
|
| 206 | +#### Browser extension - This is where magic happens |
| 207 | +
|
| 208 | +- By default, Chrome extensions cannot load remote code (JS, WASM, CSS) from CDNs because of Content Security Policy ( |
| 209 | + CSP). |
| 210 | +- Hence we had to package the runtime scripts for python inside the browser, which is |
| 211 | + this [runtime](https://github.com/SOORAJTS2001/daring-daffodils/tree/main/browser_extension/runtime) |
| 212 | +- Instead of making the entire extension in python which is very very hard (due to support),we are just injecting our |
| 213 | + python files and |
| 214 | + it's dependency into every website |
| 215 | +- Upon activating (by default it has access to all web pages) it connects to our python web socket server, and |
| 216 | + shows <img src="mobile_page/static/mouse_pointer.png" alt="Mouse Pointer" height="20px"/> which turns-out to be your |
| 217 | + new cursor! |
| 218 | +- When a `delta` is received from the mobile page, it is rescaled to match the browser’s dimensions and projected onto the browser. This causes the cursor to move according to the user’s interaction. |
| 219 | +- It would get the type of event's user has sent like scroll, drag, selection and corresponding actions are performed |
| 220 | +
|
| 221 | +##### Clicks |
| 222 | +
|
| 223 | +- if a click is fired from the user side, then a `MouseEvent` is fired on browser |
| 224 | +##### Drag |
| 225 | +- Tap for 300ms and scroll is considered as the Drag |
| 226 | +##### Selection |
| 227 | +- It could send user selected text to their connected phone |
| 228 | +- Defines a rectangle using the given screen coordinates (x1, y1) and (x2, y2). |
| 229 | +- Walks through all text nodes in the document body. |
| 230 | +- For each text node, checks whether any part of its bounding client rect intersects with the defined rectangle. |
| 231 | +- If overlapping text is found: |
| 232 | + - Collects and returns the text content (whitespace-trimmed). |
| 233 | + - Visually highlights the region by overlaying a semi-transparent blue box. |
| 234 | + - The highlight box automatically disappears after 2 seconds. |
| 235 | +
|
| 236 | +***You may have noticed that a significant part of our project is shown as JavaScript. This is because the Python runtime in the browser extension relies on JavaScript to bootstrap and interact with WebAssembly. |
| 237 | +It mainly involves two key files:*** |
| 238 | +
|
| 239 | +- **`pyodide.asm.js`** – Emscripten-generated “glue code” that initializes the WebAssembly (`.wasm`) binary and connects it to the browser’s JavaScript environment. |
| 240 | +- **`pyscript.js`** – JavaScript glue for PyScript. It integrates Pyodide with HTML elements like `<py-script>` and `<py-repl>`, enabling inline Python execution inside web pages or extensions. |
| 241 | +
|
| 242 | +***Since extensions cannot load executable code directly from the internet (for security reasons), we had to package these files locally instead of relying on CDNs.*** |
| 243 | +
|
| 244 | +## Limitations |
| 245 | +Sometimes the extension could not be used inside sites and restricts script injection, it will throw an error |
0 commit comments