People running uBlock Origin, Privacy Badger, a blocking browser, or a
DNS / VPN / router filter had no way here to see what their setup lets
through. The new tool probes a static list of widely used ad, analytics,
social, error-monitoring and device-maker endpoints plus popular tracking
scripts (fetched with no-cors and no cookies or referrer, never run), and
places bait elements for cosmetic filters. Nothing runs until the visitor
starts it; every group starts selected.
A timeout reads as "no answer" and stays out of the score, and every
listed host was checked to resolve, serve valid HTTPS and not redirect to
http:// or in a loop, since any of those would fail like a block. Element
hiding is left out of the score (DNS-level filters can never pass it) and
instead tells browser-level from network-level blocking. The page copy
says outright that blocking everything is not automatically better.
Ships with the x shortcut, locale copy for every full locale and a
changelog entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Notices across the tools are hand-rolled boxes; Alert gives new ones the
primitive instead. In this JavaScript setup the CLI imports `cn` from a
bare "cn" module and installs an unrelated npm package of that name, so
the dependency is dropped and the components import from @/lib/utils;
frontend/AGENTS.md records the trap for the next `shadcn-vue add`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A card only shows its map once location data has landed, so the bundled
placeholder maps were never meant to be seen, and their labels sat in one
language among many. An empty map URL now renders a same-sized muted box,
which also covers a payload with a country but no coordinates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any non-empty language reached Static Maps, so every distinct value was an
uncached upstream call whose answer the edge kept for a year. A tag-shape
check still passes region-tagged UI locales (zh-TW, pt-BR) without an
allow-list, and the handler block now covers both sides.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI passes its locale code as the map language, and pt-BR and zh-TW are
registered locales, but /api/map only accepted a bare two-letter code, so
every map request from those two locales answered 400 and the map never
loaded. Pass the tag through like every other ?lang (api/AGENTS.md) and
URL-encode it where the Static Maps URL is built, so the value stays one
query parameter.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
runChain's optional originalSite / accept move into an options object, so a
chain that needs only one no longer passes an `undefined` placeholder.
The per-version validators become isValidIPv4 / isValidIPv6 in
common/valid-ip.js, next to isValidIP: the "Valid" prefix keeps them apart
from the non-validating isIPv6 discriminator.
Tests now run the four single-version entry points against a stubbed fetch,
so dropping or swapping a chain's validator fails the suite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Proxy-Seller joins the paid sponsors in all six READMEs and the footer
acknowledgement list. Each sponsor's bold name in the description line now
links to the same destination as its logo, so the copy is clickable too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README listed every sponsor as an equal-size logo at the bottom, with
the only ad copy hidden in a hover-only title attribute. Paid sponsors now
get their own group with a visible one-line description under each logo;
free-plan supporters share one row of smaller logos. V.PS links carry the
README's locale in utm_content.
SwiftProxy joins the paid group and the footer acknowledgement list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Score details is one of the most-opened panels, but the score is not a
live reading. A line under it links to Blocklist Check with the IP
carried as `?q=`, which the tool now prefills without running: the
visitor may be signed out or out of quota.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Home and ToolPage are both kept alive with a Footer each, so once a tool
page had been visited, a footer link on Home opened both sheets and the
cached one, stuck on its own tab, covered the right one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A detail request still in flight when the overview reloads (Refresh, or
the re-pull on activation) wrote its answer back after the reload had
cleared the cache, so the reopened card skipped its fetch and showed the
older snapshot. Reload bumps an epoch; an answer from an earlier one is
discarded.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
Tool pages end their breadcrumb on Advanced Tools, as the <h1> beside it
names the tool. The crumb was rendered as the current page (aria-current,
not clickable) and its section ignored; now any crumb with a section links
to it, and only a bare last crumb is the current page.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
The tool page is kept alive, so a visitor returning after the backend's
5-minute refresh saw the status pulled at mount until they hit Refresh.
On activation, an overview older than that cycle reloads as Refresh does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
UserMenu raises the request from a tool, privacy or report page and goes
Home first. Home's Achievements is an async component: on a first visit it
can mount after the flag is already set, so the request was missed and the
flag stayed set, swallowing every later one. The watcher now runs
immediately and acts only on a raised flag.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
WebKit records a page's back/forward snapshot as it navigates away,
so a tool or section picked from the mobile nav sheet left a snapshot
with the sheet still sliding out, which a swipe back then showed. The
sheet closes first and the navigation follows its slide-out.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
useRoute() inside the computed ran inject() outside setup, which
returns undefined: Footer threw on every render and the navigation
back to the homepage lost its scroll restore (the page landed at the
top). The route is resolved once in setup.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
VITE_FIREBASE_AUTH_DOMAIN is each environment's own host, served by the
/__/auth proxy; the PWA-only VITE_FIREBASE_PWA_AUTH_DOMAIN and
utils/auth-domain.js go. Every OAuth provider lists the handler URL per
environment (Google and GitHub both take several redirect URIs).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
GitHub OAuth apps now take several redirect URIs, with optional
wildcard matching, so the PWA's own auth domain can be registered
there like Google's; the menu no longer hides GitHub when the PWA
signs in through a different domain (signInProvidersFor and the
store getter go). The operator notes name both registrations.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
nav.Title / PingTest / MTRTest / RuleTest had no reader in any locale.
The store takes isFireBaseSet from firebase-init.js instead of
recomputing it in checkFirebaseEnv(). The --page-bg comment no longer
claims index.html's colours must match it; they are tuned separately.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
A pass over every file the branch touched, after the iOS animation hunt
left tries and reverts behind it. Removed what nothing reaches: the
view-transition rule for a transition no code starts, an unreachable
guard in the transition hook, a no-op KeepAlive key branch, a sign-in
guard the menu already enforces, usage-dialog wrappers that only
forwarded to the store, a duplicated modifier-click check and card
filter (isPlainClick / listedTools), a test that hand-rolled
listedTools, a drawer-era fixture parameter. auth-flow.js folded into
auth-redirect.js. Comments cut to what the code cannot say itself —
file headers of a few lines, the measured iOS reasons, non-obvious
orderings — and stale ones fixed (Nav under KeepAlive, Home.vue
reusing DEFAULT_META, "from Nav", enabledCards). AGENTS.md sections
tightened to the code as it is now.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
On iOS, accelerated CSS animations running before a swipe-back stay
visually frozen once the gesture lands on the previous page: the frame
rate, the animations' own time and their iteration events all carry on,
but the compositor paints no new frame for them until their layer is
rebuilt (measured with the ?debug=nav overlay; cancel + play, a
display:none round trip or a re-insert revive them, ancestor style
changes do not). A push revives them as a side effect of its page
transition; a history step, where the browser's gesture replaces our
transition, does not. After every navigation that came from popstate,
utils/revive-animations.js cancels, replays and resets to its time each
running CSS animation, two frames after the DOM lands and again at
400ms and 1200ms in case the gesture snapshot is dismissed late.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
The Nav's CSS animations (Pulse's globe and ping) stall on iOS after a
swipe-back into the homepage, with KeepAlive and the page transitions
both ruled out and no WebKit available off the device. ?debug=nav
(kept in localStorage, ?debug=off clears) mounts widgets/NavDebug.vue:
live rAF rate, whether the ping animation's time advances and iterates,
reference CSS animations in a fixed panel and in the page flow, the
Nav's hidden state, a timeline of popstate (with hasUAVisualTransition)
/ route / visibility / focus / touch / scroll events, buttons that try
one candidate remedy at a time, and a Copy button for the log. Removed
once the cause is known.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
Route changes animate like native navigation: a tool page slides in
from the right, going home slides it back out, tool to tool cross-fades
— utils/page-transition.js decides the direction from the route pair
and wraps the router's DOM update in document.startViewTransition from
router/index.js, so the snapshot, the swap and the scroll restoration
happen before the animation and pages of different heights never
overlap. Nothing animates on a query-only change, under reduced motion,
on a history step the browser already animated (swipe-back), or in a
browser without the API; a navigation arriving mid-transition skips the
running one. The Nav opts out by view-transition-name and stays put
while the page moves.
Reapplied after the Nav's animation stall on iOS proved unrelated: it
reproduces with the transitions removed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
Since iOS 26 the system draws its Liquid Glass scroll-edge blur over
the top of a Home Screen web app whose page runs under the status bar,
which black-translucent asked for, and the blur covered the Nav. With
no apple-mobile-web-app-status-bar-style the status bar stays opaque
system chrome above the web view; the strip Nav.vue painted under a
translucent one goes with it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
Reverts the View Transitions hook-up, its keyframes, the Nav naming
and the two diagnostic follow-ups. On iOS the Nav's own animations
(Pulse's globe and ping) stalled after the forward transition and
stayed stalled after a swipe-back, while a transition back home
restored them — the capture of the fixed Nav is the suspect, and the
clean way to confirm it on the device is to run without any of it.
This reverts commits 2a360f0, d6d80da, 38a61fd, 156d0bf and 856f965.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
iOS 26+ draws its scroll-edge blur over the top of a Home Screen web app
unless a hit test at the top edge finds a fixed, viewport-wide box with
a plain background colour (WebKit's LocalFrameView::fixedContainerEdges,
which honours pointer-events on its first pass). The safe-area strip is
exactly that box; it no longer opts out of hit testing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
`?vt=off` turns the View Transitions off, `?vt=unnamed` keeps them but
leaves the Nav un-named (it slides with the page), `?vt=default`
restores the normal behaviour; the choice lives in localStorage. WebKit
cannot run in the sandbox and the Nav's animations stall on iOS after
a transition, so this is how the trigger gets bisected on the device.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco