Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/components/CommitDiff.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
export const CommitDiff = createDiffComponent({
getAdditionsElement: () =>
document.querySelector<HTMLElement>("#toc>*>strong:nth-child(2)"),
querySelectorFirst(
// 2023
"#toc>*>strong:nth-child(2)",
// 2025-10-24
"#diff-content-parent .fgColor-success",
),
getDeletionsElement: () =>
document.querySelector<HTMLElement>("#toc>*>strong:nth-child(3)"),
querySelectorFirst(
// 2023
"#toc>*>strong:nth-child(3)",
// 2025-10-24
"#diff-content-parent .fgColor-danger",
),
addSpinnerToPage(spinner) {
const container = this.getDeletionsElement()?.parentElement;
container?.appendChild(spinner);
this.getDeletionsElement()?.after(spinner);
},
getAdditionsText: (count) => i18n.t("diffs.additionsText", count),
getDeletionsText: (count) => i18n.t("diffs.deletionsText", count),
getGeneratedText: (count) => i18n.t("diffs.generatedText", count),
getGeneratedText: (count) => i18n.t("diffs.generatedText", [count]),
});
14 changes: 11 additions & 3 deletions src/components/CompareDiff.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
export const CompareDiff = createDiffComponent({
getAdditionsElement: () =>
document.querySelectorAll<HTMLElement>(".toc-diff-stats>strong")[0],
querySelectorFirst(
// 2023
[".toc-diff-stats>strong", 0],
// 2025-10-24
),
getDeletionsElement: () =>
document.querySelectorAll<HTMLElement>(".toc-diff-stats>strong")[1],
querySelectorFirst(
// 2023
[".toc-diff-stats>strong", 1],
// 2025-10-24
),
addSpinnerToPage(spinner) {
const container = this.getDeletionsElement()?.parentElement;
container?.appendChild(spinner);
},
getAdditionsText: (count) => i18n.t("diffs.additionsText", count),
getDeletionsText: (count) => i18n.t("diffs.deletionsText", count),
getGeneratedText: (count) => i18n.t("diffs.generatedText", count),
getGeneratedText: (count) => i18n.t("diffs.generatedText", [count]),
});
14 changes: 12 additions & 2 deletions src/components/PrDiff.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
export const PrDiff = createDiffComponent({
getAdditionsElement: () =>
document.querySelector<HTMLElement>("#diffstat .color-fg-success"),
querySelectorFirst(
// 2023
"#diffstat .color-fg-success",
// 2025-10-24
"*[data-component=PH_Navigation] .f6.text-bold.fgColor-success",
),
getDeletionsElement: () =>
document.querySelector<HTMLElement>("#diffstat .color-fg-danger"),
querySelectorFirst(
// 2023
"#diffstat .color-fg-danger",
// 2025-10-24
"*[data-component=PH_Navigation] .f6.text-bold.fgColor-danger",
),
addSpinnerToPage(spinner) {
const deletions = this.getDeletionsElement();
deletions?.replaceWith(deletions, spinner);
Expand Down
6 changes: 6 additions & 0 deletions src/components/createDiffComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export function createDiffComponent(options: {
generated.textContent =
" " + options.getGeneratedText(stats.exclude.changes);
generated.style.color = GREY_COLOR;
console.log(additions?.classList);
generated.classList.add(
...[...(additions?.classList ?? [])].filter(
(className) => !className.toLowerCase().includes("fg"),
),
);
const generatedAdditionsText = i18n.t("diffs.additionsSymbol", [
stats.exclude.additions,
]);
Expand Down
15 changes: 10 additions & 5 deletions src/entrypoints/content.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
const mountId = Math.random();

export default defineContentScript({
matches: ["*://*.github.com/*"],
runAt: "document_end",

main() {
main(ctx) {
main();
// TODO: schedule next interval for 1 second AFTER the main function finishes. If the main
// function takes more than 1 second, it might cause problems.
setInterval(main, SECOND);
const loop = ctx.setInterval(main, SECOND);
ctx.setTimeout(() => {
clearInterval(loop);
}, 10 * SECOND);
},
});

Expand All @@ -16,19 +21,19 @@ function main() {
if (!repo || !owner) return;

const pr = getCurrentPr();
if (pr) return replaceCount({ type: "pr", repo, owner, pr }, PrDiff);
if (pr) return replaceCount({ mountId, type: "pr", repo, owner, pr }, PrDiff);

const commitHash = getCurrentRef();
if (commitHash)
return replaceCount(
{ type: "commit", repo, owner, ref: commitHash },
{ mountId, type: "commit", repo, owner, ref: commitHash },
CommitDiff,
);

const commitRefs = getCurrentCompare();
if (commitRefs)
return replaceCount(
{ type: "compare", repo, owner, commitRefs },
{ mountId, type: "compare", repo, owner, commitRefs },
CompareDiff,
);
}
4 changes: 1 addition & 3 deletions src/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ diffs:
deletionsText:
1: $1 deletion
n: $1 deletions
generatedText:
1: $1 generated line.
n: $1 generated lines.
generatedText: $1 generated
additionsSymbol: +$1
deletionsSymbol: −$1
generatedSymbol: ⌁$1
Expand Down
2 changes: 1 addition & 1 deletion src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export const DEFAULT_CUSTOM_LIST_ALL = `*.lock
*.lock.*
*-lock*`;

export const GREY_COLOR = "var(--color-fg-muted)";
export const GREY_COLOR = "var(--color-fg-muted, var(--fgColor-muted))";
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallback on the new PR UI's grey variable if the old one doesn't exist.

export const DIFF_COMPONENT_ID = "github-better-line-counts";
14 changes: 14 additions & 0 deletions src/utils/github/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const [registerGithubService, getGithubService] = defineProxyService(
);

function createGithubService(api: GithubApi) {
const mountCache: { [mountId: number]: RecalculateResult } = {};

/**
* Returns a list of generated files that should be excluded from diff counts.
*
Expand Down Expand Up @@ -122,11 +124,18 @@ function createGithubService(api: GithubApi) {
async recalculateDiff(
options: RecalculateOptions,
): Promise<RecalculateResult> {
// Cache the result if the same content script tries to get the result multiple times.
if (mountCache[options.mountId]) {
logger.debug("[recalculateDiff] Using mount cache");
return mountCache[options.mountId];
}
Comment on lines +127 to +131
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was previously fetching the PR details every second, now it fetches only once per page load.


const ref = await getCurrentCommit(options);
const cacheKey = getCacheKey(ref, options);
const cached = await commitHashDiffsCache.get(cacheKey);
if (cached) {
logger.debug("[recalculateDiff] Using cached result");
mountCache[options.mountId] = cached;
return cached;
}

Expand Down Expand Up @@ -172,6 +181,8 @@ function createGithubService(api: GithubApi) {
include: calculateDiffForFiles(include),
};
await commitHashDiffsCache.set(cacheKey, result, 2 * HOUR);

mountCache[options.mountId] = result;
return result;
},

Expand All @@ -187,20 +198,23 @@ export type RecalculateOptions =
| RecalculateCompareOptions;

export interface RecalculatePrOptions {
mountId: number;
type: "pr";
owner: string;
repo: string;
pr: number;
}

export interface RecalculateCommitOptions {
mountId: number;
type: "commit";
owner: string;
repo: string;
ref: string;
}

export interface RecalculateCompareOptions {
mountId: number;
type: "compare";
owner: string;
repo: string;
Expand Down
10 changes: 10 additions & 0 deletions src/utils/querySelectorFirst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function querySelectorFirst(
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully this helper will reduce lines changed of future UI changes.

...selectors: Array<string | [selectAll: string, index: number]>
): HTMLElement | undefined {
for (const selector of selectors) {
const element = Array.isArray(selector)
? document.querySelectorAll<HTMLElement>(selector[0])[selector[1]]
: document.querySelector<HTMLElement>(selector);
if (element) return element;
}
}