feat: v2026.0.APR.1 — multi-stream accuracy + side panel + scheduled tests + UX polish - #3
Merged
Merged
Conversation
… 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
/ipendpoint on worker and server;Timing-Allow-Originadded so the Resource Timing API can surface DNS lookup time to the extension.chrome.alarms-driven off / 30m / 2h / daily cadence with optionalchrome.notificationson quality drop to Poor.meterx.dev/shareto be shipped separately; seeDESIGN_SESSION_GUIDE/ next iteration.)data-tipattributes — explains jitter / packet loss / bufferbloat / ping for non-technical users.Bug fixes
background.ts): non-streaming fallback computedendTime - performance.now()≈ 0 → fallback to 1s constant. Now capturesfallbackStartbeforearrayBuffer().background.ts): loaded-ping probestop()cleared the inner sleepsetTimeoutbut never called itsresolve(), soawait runnerhung forever → upload never started. Now captures and invokesresolveon stop.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.options.css): grid children defaultedmin-width:auto, pushing inputs past card edge. Fixed withmin-width:0on.isp-field+width:100%; max-width:100%; box-sizing:border-boxon inputs.popup.css):.export-wrapwasn't a flex child. Enforcedinline-flex; align-items:center; line-height:1on.history-actions > *.optional_host_permissionsdroppedhttp://*/*. Nowhttps://*/*+localhost/127.0.0.1/*.local. Options page validates URL scheme beforechrome.permissions.request.Architecture
@meterx/sharedworkspace package — all cross-package types live here (TestProgress,TestResults,HistoryEntry,IspPlan,HealthResponse,QUALITY_THRESHOLDS,USE_CASE_REQUIREMENTS).popup.tssplit 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.max-width: 1680px) — fills wider viewports instead of the previous narrow 520px column.ping 60→240,download/upload 10→120) to accommodate multi-stream test load without triggering 429s.vitest.config.mjs) + 21 unit tests covering quality grading, time formatting, share URL round-trip.Files changed
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.mjsbackground.ts,options.ts,popup.ts,manifest.json,options.html,options.css,popup.html,popup.css,build.mjs,package.json; serverapp.ts,app.test.ts,package.json; workersrc/index.ts,package.json; root.gitignoreTest plan
Automated
npm run lint— 0 errors (6 pre-existing CSS baseline warnings acceptable)npm run build— all 3 workspaces build cleanlynpm test— 31/31 passing (21 extension + 10 server)Manual — extension
packages/extension/dist/as unpacked in Chrome / Edge dev mode#r=<base64url>chrome.alarmsregistered viachrome://extensionsservice worker inspectorManual — worker / server
cd packages/worker && npx wrangler deploycurl https://meterx-speedtest.meterx-ajithakdev.workers.dev/ip→{"ip":"...","family":"ipv4"|"ipv6"|"unknown"}/ipresponse carriesTiming-Allow-Origin: *npm start→curl http://localhost:3000/ipreturns same schemaipv4Ok/ipv6Ok/edgefields (empty before redeploy)Known follow-ups (not in this PR)
meterx.dev/share(decodes#r=→ renders result card). Design brief stubbed for next session.welcome.html).🤖 Generated with Claude Code