Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
Console-style D-pad and stick navigation for any website. Browser extension and userscript.
PluginsJavaScript · HTMLGitHub @mmis1000OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Console-style spatial navigation for the web. Use your D-pad or stick to jump between links, buttons, iframes, and scrollable panels. Available as a browser extension and userscript.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Website, live demo and one-click install: https://mmis1000.me/SofaNav/
Console-style controller navigation for any web page. The D-pad and left stick
move a focus ring between links and buttons by their position on screen (like
an Xbox/TV app), not a mouse cursor.
Ships as a browser extension (Chrome, Edge, Firefox) and as a userscript built
from the same source.Read the full README ↗Website, live demo and one-click install: https://mmis1000.me/SofaNav/
Console-style controller navigation for any web page. The D-pad and left stick move a focus ring between links and buttons by their position on screen (like an Xbox/TV app), not a mouse cursor.
Ships as a browser extension (Chrome, Edge, Firefox) and as a userscript built from the same source.
extension/ the extension; content.js is the single source for everything
manifest.json MV3, runs in all frames
background.js LB/RB tab switching (extension only)
content.js all navigation logic (engine in the top frame, client in iframes)
build-userscript.js builds dist/sofanav.user.js from extension/content.js
scripts/record-demo.js records demo/sofanav-demo.mp4 with Playwright
scripts/regressions.js automated regression checks (npm run test:regress)
scripts/build-site.js assembles the website into _site/ (npm run site)
site/ the website / landing page (runs SofaNav itself)
.github/workflows/ CI: build check + regression checks, then deploys the website
dist/ generated userscript (do not edit)
test/
index.html main test page, one section per behaviour / past bug
frame.html page loaded into the test page's iframes
stress.html Twitter-sized page for performance checks
docs/
test-cases.md what to check, and every edge case / bug found so far
chrome://extensions (or edge://extensions) → Developer mode →
Load unpacked → pick extension/. Firefox: about:debugging → Load Temporary
Add-on → extension/manifest.json.npm run build, then install dist/sofanav.user.js. LB/RB do nothing there.
Each build is stamped with a hash of content.js (shown in the
description and a // Build <hash> comment under the header, logged as [sofanav] build … in the console, and in the View
on/off toast) and gets a newer @version whenever the code changed, so the
manager offers it as an update.dist/sofanav.user.js into the DevTools console.| Input | Action |
|---|---|
| D-pad | Move between elements (4-direction scored graph) |
| Left stick | Free aim at any angle (ray cast) |
| A | Click / focus a text field |
| B | Leave text field → leave container → close dialog / popover → Escape to whatever covers the page (menus, overlays) → otherwise history back |
| Hold B | Always history back (if a popup won't close) |
| L3 (click left stick) | Mouse mode on/off: left stick moves a cursor, D-pad nudges it, A clicks, right stick / LT / RT scroll under it, hover events are sent. The escape hatch for anything focus navigation can't reach |
| X | Enter the focused container (a post card, or a scrollable panel — A also enters a panel) |
| Y | Cycle graph view: off → focused item → whole screen |
| LB / RB | Previous / next tab (extension only) |
| LT / RT | Page up / down |
| Right stick | Scroll (a focused scrollable panel scrolls itself, without entering) |
| View | Turn navigation on/off |
| Menu (☰) | Debug report: log the focused element, how it's classified, and where each D-pad direction goes and why (graph / rescued / return / fallback) |
| Alt+Shift+G (keyboard) | Same as Y |
| Alt+Shift+D (keyboard) | Same as Menu (some sites swallow this shortcut; use Menu there) |
ANGLE_PENALTY, rejecting anything > MAX_DEVIATION off or merely
beside. Then a graph pass rescues elements nothing points to.AIM_TOLERANCE; wedges narrower than
MIN_WEDGE are merged. The pie in the graph view is this exact table.@sandbox DOM, Violentmonkey @inject-into content),
otherwise only same-origin frames take part. Frame
elements become proxies with translated rects, so all logic treats them alike.
A frame whose page scrolls is a container (one stop, enter with A/X); frames
that don't scroll are flattened into the page.Tunable constants are at the top of extension/content.js (stick thresholds,
repeat timing, scoring, wedge sizes); each has a comment.
npm run serve
Automated checks (Playwright, server running): npm run test:regress, and
npm run test:security (loads the real extension; forged-command attack).
Then open http://localhost:8765/test/ (the cross-origin frame loads from
127.0.0.1, so use localhost in the address). See docs/test-cases.md.
?timer drives polling with a timer instead of animation frames (for automated
runs in a background tab).window.__sofanavDebug = true, which exposes window.__sofanav
(candidates(), buildGraph(), current(), scopes(), focus(el),
state(), ...) for inspection and scripted tests.demo/sofanav-demo.mp4 (≈60 s): D-pad, graph view, stick pie, post cards, a scrollable comments panel, scrolling iframes as containers, and a non-scrolling iframe whose buttons belong to the page.
To re-record (after changes), with the test server running:
npm install
npx playwright install chromium
npm run demo
scripts/record-demo.js drives the test page with Playwright through the
keyboard stand-in and the virtual stick, adds captions and an input badge, and
writes demo/sofanav-demo.webm plus an .mp4 if ffmpeg is installed.
VERBOSE=1 npm run demo logs where focus lands after every press.
site/index.html is the landing page at https://mmis1000.me/SofaNav/. It
runs the built script itself, so visitors can try SofaNav with a controller (or
an opt-in keyboard mode). npm run build && npm run site assembles _site/:
the page, sofanav.user.js (install/update URL), sofanav-extension.zip,
the demo video and its poster. Preview locally with npm run serve:site.
.github/workflows/ci.yml runs on every push and pull request: it checks that
dist/sofanav.user.js was rebuilt from the current content.js, runs the
regression checks in Chromium, builds the site, and on main deploys it to
GitHub Pages.