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
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
The homepage sections and the advanced tools are peers, so every route
shows the same Nav, rendered once from App.vue alongside the User and
Preferences hosts. What differs is the click: on the homepage a section
link scrolls; elsewhere it navigates home and then scrolls, re-aiming
while the freshly mounted homepage still resizes; an advanced tool
entry opens /tools/<slug> from anywhere (utils/nav-target.js +
composables/use-nav-target.js). The slim page header, its tools menu
and the body padding switch it needed are gone; open sheets close on a
route change.
Tool pages, /privacy and /r/:id carry a breadcrumb above their title
(PageBreadcrumb.vue): Home steps back in history when the homepage is
the previous entry, Advanced Tools goes home to the cards, the current
page is the last crumb. Cards use an arrow instead of the drawer icon.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
Every way into a tool — the homepage cards, the nav dropdown and mobile
sheet, the keyboard shortcuts, ASN Info's profile link, the banners —
navigates to /tools/<slug>; the bottom drawer, its full-screen toggle
and the ?tool= query it was driven by are gone from Advanced.vue. The
pages stay alive (App.vue / ToolPage.vue), so browser back and forward
move between tools and the homepage, each restored as it was left.
Legacy /?tool=<slug> links redirect to the page in a router beforeEach
(utils/legacy-tool-link.js: known slug → its page with the remaining
query, unknown → the homepage). Persona Check drops noStandalone: with
the homepage kept alive its dependency commands stay registered, so the
page dispatches them in place and only a direct visit navigates home
first. The four sign-in tools no longer close a drawer before opening
the usage dialog, which PageHeader's User hosts.
Changelog entries in the six full locales; frontend/AGENTS.md describes
the page model.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
The pages outside the homepage (/tools/:slug, /privacy, /r/:id) get one
PageHeader: a back button that steps back in history when the previous
entry is the homepage (restoring the kept-alive page and its scroll) and
pushes / otherwise (utils/back-target.js), the brand, a tools menu
grouped by category (ToolsMenu.vue, over listedTools() in
utils/tool-availability.js) so a tool can be switched without going
home, the current page's title, and the account menu. Below `sm` the
row keeps the back arrow, the title and icon triggers.
The account menu is UserMenu.vue, extracted from Nav.vue, which keeps
its look; the header also hosts User and Preferences so sign-in,
benefits & usage and the preferences sheet work on every page. Each
Preferences instance opens only on the page on screen and closes when
its page is left; My Achievements off the homepage navigates home first.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
- flip pt-BR to status:'full' in the locale registry
- add pt-BR boot-screen quips and slow hint to index.html, and list it
in the JSON-LD inLanguage
- fill the productivity import-list name that landed after the pt-BR pack
- swap TRANSLATING.md's new-language walkthrough example to es-MX now
that pt-BR is a real full locale
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backfill zh-TW across all 163 changelog entries with Taiwan terminology
aligned to the UI pack, flip the registry status to full and group the
locale under Simplified Chinese as plain 繁體中文, add the zh-TW boot
copy index.html requires of full locales, and update the full-locale
enumerations in the contributor docs (including the stale 161-entry
changelog count).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
index.html can't import the registry, so its boot quips, JSON-LD and
inline language pick have always been synced by hand. A new spec now
holds them to it: full locales must cover the boot copy (beta ones are
exempt), JSON-LD inLanguage mirrors the full locales' htmlLang — fixing
the imprecise "zh" — and a sentinel evaluates the inline matcher against
matchLocale so the two can't drift apart silently. The inline pick
itself gains the same exact/base/family steps, closing the split where
?hl=zh-CN booted in one language and mounted in another.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GA shows <10 daily Turkish-language visitors after years of a native
UI, while ~100 Russian-language visitors show the longest engagement
on the site despite seeing English. Swap keeps the maintenance surface
at four locales. Stored lang=tr preferences fall back gracefully, and
MaxMind natively carries ru geo names (it never had tr ones).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Advanced tools can now be opened two ways: the existing in-page drawer, and
a real standalone page at /tools/:slug (shareable, crawlable, new-tab-able).
Same tool components render in both; only the wrapper differs.
- Router moves to HTML5 history mode: `/` → Home.vue, `/tools/:slug` →
StandaloneTool.vue. The drawer is now driven by a `?tool=<slug>` query on the
home route instead of a nested route, so ⌘/Ctrl-click, middle-click and the
drawer-header ↗ open the standalone page.
- App.vue is now a thin shell (tooltip / toast / PWA / theme + <router-view>);
the homepage body moved to the new Home.vue.
- Legacy hash links are preserved: main.js rewrites `/#/whois` → `/?tool=whois`
(and `/#/` → `/`) before the router resolves, so old bookmarks still work.
- Standalone pages drop the homepage's fixed-Nav body padding via a
`body.jn-standalone-page` class toggled on the `/tools/:slug` route.
- MtrTest / GlobalLatencyTest depend on store.allIPs (populated by the
homepage). On a standalone page that's empty, so they now fall back to a
validated free-form IP input when no known IPs exist.
- Shortcuts updated to openTool(slug) / data-adv-slug / isToolOpen; tests and
frontend/AGENTS.md updated to match.
Note: the server-side SPA fallback (so pnpm start / prod serve /tools/*) and
per-route meta land in the next change; dev / preview already have fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Nav slides up on scroll-down / down on scroll-up on mobile, with
threshold + top-of-page protection
- iOS PWA: apple-mobile-web-app-status-bar-style=black-translucent +
fixed-position safe-area painter in Nav, driven by --page-bg CSS var.
Works around WebKit's refusal to honor JS theme-color updates and
media-variant theme-color tags in standalone PWA mode.
- Sheet + Drawer primitives now respect safe-area-inset via inline
padding (top/left/right get inset-top; bottom gets inset-bottom)
- Extract theme orchestration from Preferences.vue into use-theme.js
composable mounted at App root — Preferences is now pure UI
- Align page-bg color literals across style.css (--page-bg),
index.html (html bg + meta theme-color), and manifest.webmanifest
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixed overlay (position: fixed) avoids mobile 100vh scrollbars, and
App.vue hands off in three stages — text fade, logo shrink, then
reveal #app with a scale-in — instead of display:none'ing the boot
screen instantly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two overlapping bugs:
1. In Preferences.vue, the old handleThemeChange read
`userPreferences.theme` and then called `store.setDarkMode` + class
toggles, but `prefTheme('auto')` invoked it *before* persisting the
newly-selected theme value, so switching to Auto could stick on the
previous manual state. The refactor funnels every trigger (mount / OS
flip / preference change) through a single `applyTheme()` that reads
the current values and applies them once. A `watch` on the theme pref
keeps the handler firing even if future code paths mutate it directly,
and `onUnmounted` removes the matchMedia listener for cleanliness.
2. In index.html, the inline `<style>` had a
`@media (prefers-color-scheme: dark) { html { background: #0a0a0a } }`
override. That rule is gated strictly on the OS preference and ignores
the `.dark` class that Tailwind (and Preferences.vue) use to control
dark mode. With system=dark + user=light, Tailwind correctly flipped
to light but the inline media query kept the `<html>` background
painted dark — which then showed through every gap in the light-mode
layout under the Sheet overlay, producing the UI in the report.
Swap the media query for `html.dark { ... }` so the inline CSS now
respects the same class-based switch. Seed that class before first
paint via a short inline script at the top of `<head>` — reads
`localStorage.userPreferences.theme`, falls back to
`prefers-color-scheme` on auto, adds `.dark` if needed. This keeps the
boot screen and the mounted app in agreement from frame 1 (no FOUC,
no flash).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The page shook on initial load because Tailwind v4 Preflight arrived after the
browser's first paint and invalidated the box model and text metrics the boot
screen had been drawn with:
* body margin 8px → 0 ≈ 8px shift up + left
* *, ::before, ::after { box-sizing: border-box } ≈ .jn-spinner 28×28 → 20×20,
content below jumps up 8px
* html line-height normal → 1.5 ≈ every line grows 4-6px
Fix by mirroring the exact Preflight subset the boot screen depends on in an
inline <style>, so the stylesheet's arrival causes zero visual change. Also
reserves the scrollbar width up-front via `scrollbar-gutter: stable` so the
eventual app scrollbar never shifts the page sideways.
Other cleanup while here:
- Deduped viewport / favicon / title meta that had drifted into two copies.
- Dropped invalid SVG attrs (text-rendering, image-rendering) that applied to
nothing.
- Moved the 12s "slow network" hint off `innerHTML = ...` onto a `<p hidden>`
that simply flips `hidden = false` — same UX, no string parse, smaller XSS
surface.
- Added a `<noscript>` fallback and ARIA labelling (role=status / aria-live /
aria-hidden on the decorative logo / aria-label on the spinner).
- Boot screen tracks `prefers-color-scheme: dark` via media query so pre-Vue
dark-mode users don't flash a white screen.
- Annotated the two DOM contracts (`#jn-loading`, `meta[name=theme-color]` +
`meta[name=background-color]`) so future edits don't accidentally break
`App.vue` / `Preferences.vue`.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Advanced Tools panel gets the modern treatment:
- The 9 tool tiles are shadcn <Card> with keyboard-shortcut-card jn-card
markers; hover -translate-y-1.5; semantic tokens throughout; emoji
hover lift+scale preserved as MyIP's playful signature
- Bottom Sheet (reka-ui) → vaul-vue Drawer: drag-to-close + body-scale
iOS-like depth feeling
- Drawer wrapper turns off setBackgroundColorOnScale to avoid vaul's
hard-coded black body bg breaking light mode
- Fullscreen toggle: added transition: transform + height mixed transition
- Advanced cards no longer participate in keyboard navigation (opening a
sub-page has a different semantic than the homepage status cards)
Section header + note paragraph use the same class rhythm as the other
4 refactored modules; the tile labels, descriptions, and layout are
unified.