Commit Graph
35 Commits
Author SHA1 Message Date
Claude 96525d2fc7 Chore: trim the branch — leftovers, redundancy, comments
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
2026-10-05 16:50:16 +00:00
Claude 2f5d3b426b Feat(tools): open the Advanced Tools as pages
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
2026-10-05 14:25:02 +00:00
jason5ng32andClaude Opus 5.5 c29e48687b Refactor(layout): split backend-only code out of common/, group frontend utils
common/ had become the backend's library: 30 of its 41 modules were
imported by nothing in the frontend, so the name no longer said what
belonged there. It now holds only what the frontend or the build imports
too; backend-only code moved to server/, flat except for datasets/ (the
dataset engine and its readers). The *-db/ dataset directories stay in
common/: self-hosting docs and Docker volumes mount those paths.

frontend/utils/ keeps its app-wide modules at the root and gains ip/
(IP / ASN shaping and validation), report/ (the report pipeline) and
features/ (the pure layer of a single feature). Paths only — no logic
changes. The Dockerfile copies server/, and the AGENTS.md files say where
a new module goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:10:02 +08:00
jason5ng32andClaude Fable 5 6433784309 Fix(ui): keep country-picker sticky tally flush once results render
sticky top-0 resolves against the scroll container's content box and
never covers the container's own padding, so the results-state layout
(where the padded wrapper itself became the scroll container) pushed
the tally down by py-3. Split overflow and padding into separate
layers so the scroll container is always padding-free, in all three
Globalping-backed tools.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 19:42:02 +08:00
jason5ng32andClaude Opus 5 f0a424eed0 Feat(ip-forms): reject unreachable targets before any request goes out
QueryIP, MtrTest and GlobalLatencyTest all took any well-formed IP and
spent a round trip discovering that nobody can answer for it: a LAN
address walks every geo source collecting the same "no data", and no
Globalping probe has a path to one from any country.

All three now answer locally. The two Globalping tools share
classifyTarget(), which returns empty / invalid / unreachable / ok so the
input can both style itself and say why a well-formed address was
refused — a silently greyed-out Run button reads as a bug. QueryIP also
drops its error message once the box is emptied, since the complaint no
longer describes anything on screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:30:04 +08:00
jason5ng32 37f2e00e80 Improvements 2026-07-28 20:58:52 +08:00
jason5ng32andClaude Fable 5 b2c22a98b1 Feat(ui): shared Globalping country picker for MTR / latency / censorship
New GlobalpingCountryPicker renders the caller's suggestion sections plus
the full per-continent catalog of probe-having countries (from the backend
inventory), all over one v-model selection with section-level select-all
and a sticky tally. Caps are soft: past the limit (30 for MTR/latency, 12
for censorship) the tally turns red and the run button locks. MTR and
Global Latency keep their curated 28-country spread as the default-selected
suggestion; CensorshipCheck migrates its inline picker to the component and
gains the free catalog. Left picker column matches the results column's
height once results exist (absolute-fill trick, min-height floor).
Also folds CensorshipCheck's render-function table back into the template.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 09:00:54 +08:00
jason5ng32 34f54b7b4a Improvements 2026-07-24 18:28:50 +08:00
jason5ng32 0ccd23f698 UI Improvement 2026-07-20 09:33:18 +08:00
jason5ng32andClaude Fable 5 eae285caf2 Feat(report): emit full test results and collect report snapshots
Every "my network" test now emits a <domain>:finished event with its
complete structured result on the app bus (existing thin events keep
their fields — achievement rules are untouched). Connectivity and
WebRTC additionally record locale-free status/NAT enums next to their
localized labels so downstream consumers never parse t() output.
A collector mounted once in App.vue normalizes each payload through
per-section builders (shape-based cleaning, no free text) and keeps
the latest schema-valid snapshot per section for the share dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 12:24:21 +08:00
jason5ng32andClaude Fable 5 11b6ed81a3 Feat(mtrtest): render MTR results as a structured hop table
Raw mtr text was hard to scan and useless on mobile. Parse each probe's
output (header-driven column mapping, tolerant of differing mtr builds)
and render a compact table: adaptive Host/IP/ASN identity columns plus
whatever numeric columns the data carries; falls back to the raw text
when nothing is parseable. Table headers localized in all four locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 12:01:08 +08:00
jason5ng32 8d5e8260ff Improvements 2026-06-08 15:12:55 +08:00
jason5ng32 f2d0405a9a Improvements 2026-06-08 15:09:48 +08:00
jason5ng32 09750b5008 Improvements 2026-06-08 14:53:21 +08:00
jason5ng32 3fcb0c72c3 Improvements 2026-06-08 14:09:21 +08:00
jason5ng32 8eea61d217 Improvements 2026-06-08 13:59:02 +08:00
jason5ng32andClaude Opus 4.8 dea0cc157e Feat(advanced-tools): standalone /tools/:slug pages + history routing
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>
2026-06-08 13:00:19 +08:00
jason5ng32andClaude Opus 4.8 3eac8b7443 Refactor(frontend): rename use-analytics to analytics
It is a module-level analytics service with no Vue reactivity — the use- prefix wrongly implied a composable. Renamed utils/use-analytics.js to utils/analytics.js and updated all 29 importers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:04:17 +08:00
jason5ng32andClaude Opus 4.8 94ffddc9a2 Refactor(advanced-tools): share Globalping default locations and selectable IPs
Extract the verbatim-identical 16-country default locations array and the
store.allIPs filter from MtrTest and GlobalLatencyTest into the shared
use-globalping-measurement composable as GLOBALPING_DEFAULT_LOCATIONS and
selectableIPs(). Pure data/computed extraction; behavior unchanged.
CensorshipCheck is intentionally left untouched (per-country probe limits).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 10:03:22 +08:00
jason5ng32 b6fba97956 Improvements 2026-05-28 17:30:04 +08:00
jason5ng32 5fcb1dd2fe UX Improvements 2026-05-27 02:46:56 +08:00
Claude 0f03f2c9cc Refactor(advanced-tools): extract useGlobalpingMeasurement composable
MtrTest / GlobalLatencyTest / CensorshipCheck all hand-rolled the same
"POST to Globalping → poll GET by id → retry while in-progress" loop,
each with its own setTimeout bookkeeping and no unmount cleanup. If the
user closed the drawer mid-poll, timers kept firing and mutated refs on
unmounted components.

Factor that orchestration into frontend/composables/use-globalping-
measurement.js. The composable wraps POST + GET through fetchWithTimeout
(so a hung upstream no longer pins the request forever) and clears any
pending timer / aborts any in-flight controller on onScopeDispose.

Per-call-site result parsing is kept as an onResults callback so each
tool's field mapping stays explicit. CensorshipCheck's two-message error
split (POST vs empty/poll) is preserved via onError(reason).

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-20 15:36:14 +00:00
jason5ng32 5efab6172c Improvements of UI 2026-04-19 09:58:34 +08:00
jason5ng32 8a80724855 Improvements 2026-04-19 08:31:26 +08:00
jason5ng32 f8c5851686 Improve comments 2026-04-19 08:24:49 +08:00
jason5ng32andClaude Opus 4.7 109c3ba9a5 refactor(mtrtest): switch to InputGroup + Accordion card-style + semantic colors
+ Incidentally adds a success variant to Badge

- Input row uses InputGroup wrapping Select + Button; SelectTrigger's
  inner button is flattened by the group for seamless stitching
  (original used rounded-r-none + -ml-px to do it by hand)
- Button bg-blue-600 hard color → variant="action" + <Spinner /> + :disabled
- Accordion items become independent rounded-lg border bg-card px-4 +
  hover:no-underline
- Trigger content: circle-flag + country/city + · separator + muted
  network name + AS Badge (variant="success" replacing the original
  bg-green-600 dark:bg-yellow-500 light/dark hard colors)
- Raw pre block: bg-black / bg-neutral-100 light/dark alternation →
  unified bg-muted tracking theme
- Error notice bg-sky-50 + dark:bg-sky-950 → bg-info/10 + border-info/30 + text-info
- Flags .fi fi-* → circle-flags

Badge success variant added:
- border-transparent bg-success text-success-foreground shadow
- Combined with rounded-full + shadow-none, makes a low-key green badge

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:44:28 +08:00
jason5ng32andClaude Opus 4.6 0da4da3362 refactor: rewrite MtrTest to Tailwind + shadcn (17/N for C.2)
- IP picker: Bootstrap <select class=form-select> → shadcn Select.
- Run button: .btn.btn-primary → <Button>.
- AS badge inside Accordion trigger: .badge.rounded-pill.text-bg-
  success/warning → shadcn <Badge> + rounded-full + color classes.
- Accordion internals already migrated in C.1 (just cleaned the
  remaining bg-black/bg-light/text-light classes on the pre
  container).
- .alert.alert-info error → Tailwind sky color block.
- Spinner → animate-pulse dot.

Verified: npm run check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:38:20 +08:00
jason5ng32andClaude Opus 4.6 7320cb42b1 refactor: bootstrap collapse/accordion → shadcn-vue Collapsible/Accordion
refactor/01 phase C, commit 1/3. Replaces the last four uses of Bootstrap
Collapse (which also fixes the content-goes-blank-after-expand bug the
user reported at the end of phase B).

New UI components:
- frontend/components/ui/collapsible/ (Collapsible, CollapsibleTrigger,
  CollapsibleContent) — based on reka-ui CollapsibleRoot
- frontend/components/ui/accordion/ (Accordion, AccordionItem,
  AccordionTrigger, AccordionContent) — based on reka-ui AccordionRoot,
  defaults to type=single + collapsible=true to match the Bootstrap
  accordion-with-data-bs-parent semantics

Animations:
- Add CSS keyframes jn-collapsible-down/up and jn-accordion-down/up to
  style.css, driven by reka-ui's --reka-collapsible-content-height and
  --reka-accordion-content-height CSS variables. 200ms ease-out on
  [data-state] transitions.
- Chose custom keyframes over tw-animate-css's built-ins to avoid
  cross-contamination with existing Bootstrap .collapse / .collapsing
  rules while both frameworks coexist.

Migrated components:
- ip-infos/IPCard.vue        — ASN info Collapsible (single toggle per
  card). Simplified local state: collapseStates dict → plain isAsnOpen
  ref (each IPCard instance tracks its own one-shot panel).
- ip-infos/ASNInfo.vue       — removed .collapse class from root (the
  panel is now controlled by the parent CollapsibleContent wrapper).
- advanced-tools/Whois.vue   — Accordion over providers. type=single,
  collapsible, default-value="0" so the first provider is expanded,
  matching the previous behavior.
- advanced-tools/MtrTest.vue — Accordion over per-country MTR results,
  same pattern as Whois.
- advanced-tools/SecurityChecklist.vue — two collapses:
  1) Category intro: wrapped in Collapsible around the descriptive
     paragraph + trigger icon.
  2) Per-item info row: the table structure (header tr + colspan-4 tr)
     doesn't compose cleanly with CollapsibleRoot, so this one uses a
     plain checklistInfoOpen[index] ref + v-show instead. No animation,
     but correct behavior.

Scan verified: zero `data-bs-toggle="collapse"`, `accordion-button`,
`accordion-item`, `data-bs-target="#collapse*"` remaining.

Bug fix: the "content goes blank after expand" issue observed at the end
of phase B is resolved as a side effect — the content is no longer
managed by Bootstrap's Collapse JS that was conflicting with Tailwind
Preflight rules.

Verified: npm run check — 46 tests pass, vite build succeeds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:30:05 +08:00
jason5ng32 469e71da46 Improvements 2024-09-26 18:11:28 +08:00
jason5ng32 e4743116f6 Improvements 2024-08-09 17:53:24 +08:00
jason5ng32 559fd9778c Improvements 2024-08-09 09:42:05 +08:00
jason5ng32 80377ca35c Lang Improvements 2024-05-23 11:12:11 +08:00
jason5ng32 1cd97891bf 完成 Advanced 部分的组合式 API 改造 2024-05-13 11:24:16 +08:00
jason5ng32 833064b2c5 完成 Advanced 部分的组合式 API 改造 2024-05-13 11:23:06 +08:00
jason5ng32 20ca2045b4 多语言和 GA 的局部引入改造完成 2024-05-13 10:13:20 +08:00