尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Compare segments on the same Strava activity (you vs a friend or vs your PRs)
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Chrome extension to compare two Strava activities by their segments. It can auto-detect activity tabs you already have open, read segment data from each activity, compute time and speed (or pace) deltas, and export the results as CSV. It runs entirely in your browser; no servers are involved.
If you have questions or issues, please open an issue on GitHub.查看完整 README ↗Chrome extension to compare two Strava activities by their segments. It can auto-detect activity tabs you already have open, read segment data from each activity, compute time and speed (or pace) deltas, and export the results as CSV. It runs entirely in your browser; no servers are involved.
If you have questions or issues, please open an issue on GitHub.
The comparison table is a plain, readable table, sortable by any column, with time and speed/pace deltas shown as both a colour and an arrow.
chrome://extensions/Required permissions: storage, and host access to https://www.strava.com/* (see manifest.json). There is no tabs permission: host access already shows the extension the URLs of your Strava tabs, and no others.
Every delta is activity 2 minus activity 1, and the PR and history columns read activity 1's times, so which is which matters. The ⇄ button between the URL fields swaps them. If a comparison is showing, it is redone the other way round so the table matches the fields. That re-reads both activities, and any PR columns need “Compare vs my PRs” again, which comes from the cache.
Fill in (or auto-detect) Activity 1 only, then click “My Activities Here”. It reads activity 1’s segments, looks up your effort history on each, and lists up to five of your other activities that share the most of them, with their date and how many segments they share. Click one to fill Activity 2 and compare.
Above the table you get the net gap, how many segments each athlete took, and the biggest three losses and gains by name. The net is a plain sum of the per-segment deltas — the sense in which people say “I lost three minutes” — so a long segment contributes more to it than a short one. It is not weighted by segment length.
The Time Diff % column is the length-fair reading: each segment's time difference as a share of activity 1's time there. Ten seconds is 20% on a 50-second sprint and under 1% on a twenty-minute climb, so sorting by it puts the segments where activity 2 was relatively slowest on top.
“Copy summary” puts the same reading on your clipboard as plain text: who was faster and by how much, the faster/slower counts, and the biggest losses and gains. It follows the nested-segments choice, so it says what the panel says.
Under the counts, a bar per segment in course order shows the running total: above the dashed line activity 2 is behind, below it is ahead. Hovering a bar names the segment and the total there. It answers what the net cannot — whether the gap opened in one place or everywhere.
By default a segment inside another (a climb within a lap, a lap within a full-route segment) counts in both, since overlapping segments are often the interesting ones. Where Strava's page says where each effort sits in the ride, nested segments are marked inside … under their name and the summary offers to leave them out, which recounts the net and redraws the chart without counting any stretch of road twice. The table always keeps every row. The activity log says on each comparison whether positions were available at all.
“Compare vs my PRs” also fills a Your history column: where activity 1's effort ranks among all your efforts on that segment (“3rd of 12”), and a sparkline of your times from oldest to newest with your best marked. Faster is drawn higher, and each segment is scaled to its own range, so the shape is your progression there rather than a comparison between segments. Up to 60 of your most recent efforts are drawn; beyond that the label says “of last 60”.
When both activities recorded heart rate, a Form column reads the two deltas together, which neither column says on its own:
| Reading | What happened |
|---|---|
| Fitness | Faster at the same or a lower heart rate, or the same time at a lower one |
| Effort | Faster, but at a higher heart rate |
| Even | Same time, same heart rate |
| Easier | Slower at a lower heart rate |
| Slower | Slower at the same heart rate |
| Fading | Slower at a higher heart rate |
A second of timing and two beats per minute are treated as noise, so riding a segment the same way twice reads Even.
Click any column header to sort by it; click the same header again to reverse. Segments with no value in that column always sink to the bottom, in both directions, so an unreadable row never ranks as the fastest. The default order is activity 1’s page order, which is the order you rode them.
“Compare vs my PRs” adds two columns: your personal record on each segment, and
how far activity 1 was off it. The PR is your all-time best, so if activity 1
is your own PR ride it shows 0:00; a negative value means activity 1 beat
your PR.
Some caveats worth knowing:
/athlete/segments/{id}/history, the JSON Strava’s own site uses), and the
PR is your fastest elapsed time there. If that endpoint fails, the segment’s
page is read instead, and the activity log says so and whyClear does not empty that cache;
“Refresh my PRs” reads every segment again, ignoring it, which is how a PR set
since the last lookup shows up. A segment that fails to refresh keeps the copy
it hadN/A. That means "unknown", not
"no PR" — this reads what Strava serves rather than guessingClick “Export CSV” to download a CSV with headers that include the detected athlete names. It contains exactly the columns the table is showing, in the sort order you left it in.
Shading intensity scales with the size of the delta: a full-strength tint is 60s of time, 10% of time, 5 km/h of speed, 30 s/km of pace, 50 W of power, or 200 m/h of VAM.
VAM is computed as distance × average grade ÷ time, for both activities alike.
Strava fills in its own VAM only for categorized climbs, and it means little on
flat ground, so segments averaging under 3% show N/A. On one segment the climb
is the same for both activities, so the VAM delta is the time delta expressed in
climbing units — useful for comparing across climbs, not new information within
one.
Getting the data for one activity takes the cheapest route that works, and falls back automatically when a route fails:
DOMParser.A strava.com tab is only used for route 2 if its content script answers. Tabs opened before the extension was installed or updated are skipped until you reload them.
Segment data comes from the inline script that seeds Strava’s efforts list
(pageView.segmentEfforts().reset(…)) rather than from the table: it has raw
numbers (times, heart rate, power, grade), reads the same in every language and
unit system, and is in the page’s HTML even for runs, whose table is only drawn
after the page loads. The table is read only when that data is missing.
Files:
extractor.js: all DOM reading. Every function takes an explicit Document, so the same code runs against a live page or against fetched HTMLcontent-script.js: request/response bridge on strava.com — extract this page, fetch another activity, or look up your effort history on a segment (your PR and recent activities there; the segment page is the fallback for the PR). It only fetches from an allowlist of paths, and waits for the segments table with a MutationObserver instead of a fixed sleeppopup.js: tab detection, route selection, comparison, rendering, CSV export. The table's columns are declared once in COLUMNS, which drives the headers, the cells and the CSV togetherutils.js: pure parsing and comparison helpers (times, speeds, paces, power, heart rate, VAM, distances, segment matching, sorting, summary, ranking your activities)background.js: MV3 service worker. Its one job is closing the background tabs a popup opened, if the popup is closed before it couldnpm install
npm test
utils.js and extractor.js are covered by unit tests; tests/popup.test.js
loads the real popup.html and popup.js into jsdom with a stubbed chrome
API and exercises the full comparison and rendering path. tests/background.test.js
plays the popup's side of the service worker's port, including closing mid-run.
GitHub Actions runs npm test and npm run build on every push to main and
every pull request, and keeps the packaged zip as a build artifact.
tailwind.output.css is hand-maintained plain CSS — a small subset of
Tailwind-style utilities plus the extension's own component classes. Edit it
directly. There is no CSS build step and no Tailwind dependency: the name is
historical, and a tailwind.css source, a Tailwind config and a PostCSS config
all used to sit here without ever being run.
Project structure (selected):
manifest.json: Chrome MV3 manifest (name, version, permissions)popup.html, popup.js, tailwind.output.css: Popup UI and logicextractor.js, content-script.js: Reading data out of Strava pagesutils.js: Parsing and comparison helperstests/: Vitest suitesicons/: Extension iconstabs permission, so tabs on other
sites are invisible to it — host_permissions limits it to www.strava.comchrome.storage.local to auto-restore the last viewhttps://www.strava.com/activities/<id> pages. Only Strava tabs are visible to the extensionMIT — see LICENSE.md.