尚未偵測到更新。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.