Skip to content

feat: v2026.0.APR.1 — multi-stream accuracy + side panel + scheduled tests + UX polish - #3

Merged
ajithakdev merged 1 commit into
mainfrom
add/dev/2026.0.APR.1
Apr 22, 2026
Merged

ajithakdev merged 1 commit into
mainfrom
add/dev/2026.0.APR.1

Conversation

@ajithakdev

Copy link
Copy Markdown
Owner

Summary

Major feature drop + accuracy overhaul, aiming to make MeterX a must-have network tool for both technical and non-technical browser users.

Headline changes

  • Accurate measurement: multi-stream (4 parallel DL, 3 parallel UL) duration-based tests with ramp-up and tail trimming — matches M-Lab/Ookla methodology. Saturates fat pipes the old single-stream 10MB couldn't reach.
  • Bufferbloat grade (A–F): loaded-ping probe during download yields a latency-under-load delta vs idle ping.
  • DNS timing + IPv4/IPv6 detection: new /ip endpoint on worker and server; Timing-Allow-Origin added so the Resource Timing API can surface DNS lookup time to the extension.
  • Side panel dashboard: MV3 side panel with multi-series SVG trend chart, ISP comparison, history, and a run button. Persistent while browsing.
  • Scheduled background tests: chrome.alarms-driven off / 30m / 2h / daily cadence with optional chrome.notifications on quality drop to Poor.
  • ISP plan comparison: user enters advertised plan in Options → popup and side panel show delivered % of paid.
  • Shareable result URLs: hash-encoded base64url payload, stateless — no server storage required. (Viewer page at meterx.dev/share to be shipped separately; see DESIGN_SESSION_GUIDE / next iteration.)
  • JSON + CSV export alongside PDF for machine-readable workflows.
  • Plain-language tooltips via data-tip attributes — explains jitter / packet loss / bufferbloat / ping for non-technical users.

Bug fixes

  • Download timing bug (background.ts): non-streaming fallback computed endTime - performance.now() ≈ 0 → fallback to 1s constant. Now captures fallbackStart before arrayBuffer().
  • Test hanging after download (background.ts): loaded-ping probe stop() cleared the inner sleep setTimeout but never called its resolve(), so await runner hung forever → upload never started. Now captures and invokes resolve on stop.
  • Export menu scrollbar (popup.css): absolute-positioned dropdown pushed popup body into scroll. Now a fixed-position centered overlay with dim backdrop, Escape-to-close, click-backdrop-to-close.
  • ISP input overflow at 175% zoom (options.css): grid children defaulted min-width:auto, pushing inputs past card edge. Fixed with min-width:0 on .isp-field + width:100%; max-width:100%; box-sizing:border-box on inputs.
  • Share/Export button baseline misalignment (popup.css): .export-wrap wasn't a flex child. Enforced inline-flex; align-items:center; line-height:1 on .history-actions > *.
  • Manifest permission overreach: optional_host_permissions dropped http://*/*. Now https://*/* + localhost/127.0.0.1/*.local. Options page validates URL scheme before chrome.permissions.request.

Architecture

  • New @meterx/shared workspace package — all cross-package types live here (TestProgress, TestResults, HistoryEntry, IspPlan, HealthResponse, QUALITY_THRESHOLDS, USE_CASE_REQUIREMENTS).
  • popup.ts split from 717 lines into focused modules: quality.ts, history.ts, format.ts, export-pdf.ts, export-data.ts, share.ts, sparkline.ts, tooltips.ts, isp.ts, chart.ts.
  • Options page converted to responsive auto-fit grid (max-width: 1680px) — fills wider viewports instead of the previous narrow 520px column.
  • Server rate limits bumped (ping 60→240, download/upload 10→120) to accommodate multi-stream test load without triggering 429s.
  • Extension added vitest config (vitest.config.mjs) + 21 unit tests covering quality grading, time formatting, share URL round-trip.

Files changed

  • Added (24): packages/shared/*, packages/extension/src/chart.ts, export-data.ts, export-pdf.ts, format.ts, format.test.ts, history.ts, isp.ts, quality.ts, quality.test.ts, share.ts, share.test.ts, sidepanel.ts, sparkline.ts, tooltips.ts, static/sidepanel.html, static/sidepanel.css, vitest.config.mjs
  • Modified (13): extension background.ts, options.ts, popup.ts, manifest.json, options.html, options.css, popup.html, popup.css, build.mjs, package.json; server app.ts, app.test.ts, package.json; worker src/index.ts, package.json; root .gitignore

Test plan

Automated

  • npm run lint — 0 errors (6 pre-existing CSS baseline warnings acceptable)
  • npm run build — all 3 workspaces build cleanly
  • npm test — 31/31 passing (21 extension + 10 server)

Manual — extension

  • Load packages/extension/dist/ as unpacked in Chrome / Edge dev mode
  • Click toolbar icon → popup opens, quality badge, sparkline, stats render
  • Click Start Test → full DL / UL / Ping sequence runs start-to-finish (no hang after DL) in ~17–20s
  • Verify DL/UL numbers are in same ballpark as Google / fast.com (multi-stream should now saturate)
  • Click Export → overlay appears centered, no body scroll; try PDF / JSON / CSV each
  • Click Share → clipboard copies URL containing #r=<base64url>
  • Open Options page → responsive layout fills ultrawide screen; cards side-by-side ≥ 720px, stacked below
  • Zoom browser to 175% on Options → ISP inputs stay inside their card
  • Enter ISP plan (e.g. 300 / 100) → save → reopen popup → ISP comparison card shows % of paid
  • Set scheduled tests to "Every 30 min" + tick Notify → save → verify chrome.alarms registered via chrome://extensions service worker inspector
  • Open side panel (via Chrome side-panel UI) → trend chart + ISP compare + history render
  • Run second test → side panel live-updates without reload
  • Hover / focus a stat label → tooltip appears with short plain-language copy

Manual — worker / server

  • Redeploy worker: cd packages/worker && npx wrangler deploy
  • Verify curl https://meterx-speedtest.meterx-ajithakdev.workers.dev/ip → {"ip":"...","family":"ipv4"|"ipv6"|"unknown"}
  • Verify /ip response carries Timing-Allow-Origin: *
  • Self-host server: npm start → curl http://localhost:3000/ip returns same schema
  • Run an extension test after worker redeploy → JSON export now populates ipv4Ok / ipv6Ok / edge fields (empty before redeploy)

Known follow-ups (not in this PR)

  • Share viewer page at meterx.dev/share (decodes #r= → renders result card). Design brief stubbed for next session.
  • First-install onboarding HTML (welcome.html).
  • Chrome / Edge store promotional tiles + listing copy.
  • OG preview image template for shareable URL embeds.

🤖 Generated with Claude Code

… tests, UX polish

Measurement accuracy
- Replace single-stream DL/UL with M-Lab/Ookla-style multi-stream duration-based
  measurement: 4 parallel download streams + 3 parallel upload streams, 8s
  window, 1.2s ramp-up trim, 0.5s tail trim, linear-interpolated byte samples
  for steady-state throughput
- Fix DL timing bug on non-streaming fallback path (endTime - performance.now()
  always ~= 0 -> 1s fallback; now captures fallbackStart before arrayBuffer)
- Loaded-ping probe during download yields bufferbloat A-F grade (delta vs
  idle ping)
- Extract DNS lookup time from Resource Timing API (worker + server now set
  Timing-Allow-Origin)
- Detect IPv4 vs IPv6 family via new /ip endpoint on worker and server

Architecture
- Add @meterx/shared workspace package for cross-package types
  (TestProgress, TestResults, HistoryEntry, IspPlan, HealthResponse, quality
  + use-case thresholds)
- Split 717-line popup.ts into focused modules: quality.ts, history.ts,
  format.ts, export-pdf.ts, export-data.ts, share.ts, sparkline.ts,
  tooltips.ts, isp.ts, chart.ts
- Scope manifest optional_host_permissions (drop http://*/*; keep https://*/*
  plus localhost/127.0.0.1/*.local). Options page validates URL scheme
  before chrome.permissions.request

Side panel
- New sidepanel.html dashboard with multi-series trend chart (inline SVG, no
  deps), ISP compare card, history list, run-test button
- Live-updates via chrome.storage.onChanged + runtime message listener

New features
- MV3 side_panel manifest entry
- Scheduled background tests via chrome.alarms (off / 30m / 2h / daily)
  with optional chrome.notifications on quality-drop to Poor
- ISP plan comparison (download/upload % of advertised) stored in sync
- Shareable result URLs — hash-encoded base64url payload, stateless
- JSON + CSV export alongside PDF
- Popup sparkline for recent download trend
- Plain-language tooltips via data-tip attributes

UX fixes
- Export menu now a centered fixed-position overlay with dim backdrop
  (was absolute dropdown that forced popup body scroll)
- ISP plan input fields no longer overflow card at 175% zoom (grid children
  need min-width:0 + width:100%/box-sizing:border-box)
- Options page fills wider viewports via responsive auto-fit grid (max-width
  1680px, collapses to single column < 720px)
- Share/Export buttons align on same baseline via inline-flex on wrapper
- Tooltip copy trimmed to 30-50 chars each
- Loaded-ping probe stop() no longer hangs: captures inner-sleep resolve so
  await runner completes immediately when stopped (this was the cause of
  the "Testing..." forever state after download completed)

Server / worker
- Worker: /ip endpoint reflecting connecting IP + family; Timing-Allow-Origin
  on all CORS responses; /ip route wired
- Server: /ip endpoint with same contract; rate limits bumped (ping 60->240,
  download/upload 10->120) to accommodate multi-stream test load

Tests
- Add vitest suite for extension (21 tests): quality grading, time-ago
  formatting, share URL round-trip
- Extend server app tests (+2) covering /ip endpoint + Timing-Allow-Origin

Chore
- .gitignore: exclude .claude/settings.local.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ajithakdev
ajithakdev merged commit d8bebb9 into main Apr 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant