Migrate frontend to Svelte 5 + Vite + TypeScript#6
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
📜 Recent review details🔇 Additional comments (10)
📝 WalkthroughWalkthroughThis PR adds a Svelte/Vite frontend dashboard and integrates Bun-based frontend compilation into the existing Python single-file build. It updates hooks, Makefile, and CI/release workflows to run the frontend build, replaces template.html with web/dist/index.html as the injected template, and introduces a full set of UI components, types, styles, utilities, and mock data. ChangesFrontend dashboard and build system integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
Replace the 1,740-line vanilla-JS template.html with a componentized, type-safe frontend built with Bun + Vite. The output is still a single self-contained HTML file embedded into dist/repo-intel by build.py, so the end-user contract is unchanged: a zero-dependency Python executable. Bun/Vite are build-time only. - web/: Svelte 5 + Vite + TypeScript app. vite-plugin-singlefile inlines all JS+CSS into web/dist/index.html, which keeps the /*__DATA_INJECTION__*/ marker (re-inserted post-build by a small Vite plugin) so repo-intel.py's injection is unchanged. - The dashboard engine is split into focused, typed modules under web/src/lib/ (heatmap, timeline, charts, table, popovers, header, interactions + helpers), ported to parity from the old template. - Chart.js is now bundled instead of loaded from a CDN — the output is fully offline/self-contained. - build.py + repo-intel.py read web/dist/index.html instead of template.html. - Makefile gains web-build/web-dev; pre-commit hook rebuilds the bundle and watches web/ (needs Bun); ci.yml/release.yml add oven-sh/setup-bun pinned to 1.3.10 for reproducible builds. - Delete template.html; update README, .gitignore. Verified end-to-end via python3 dist/repo-intel against this repo and a 3-contributor mock: zero console errors, full visual parity.
Replace `chart.js/auto` with explicit Chart.register() of only the controllers/elements/scales/plugins this dashboard uses (line, bar, doughnut + their elements, linear/category scales, Title/Tooltip/Legend/Filler). Drops the unused controllers (radar, polar-area, scatter, bubble) and the radial scale. web/dist/index.html: 303.7 kB -> 280.0 kB (gzip 102.5 -> 95.2 kB). Verified in-browser with the 3-contributor mock: all chart types render with no "not a registered ..." runtime errors.
6be679f to
6bb738e
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (4)
web/src/lib/heatmap.ts (2)
12-18: ⚡ Quick winConsider extracting hardcoded background colour to theme.
The hardcoded RGB values
{ r: 22, g: 27, b: 34 }(line 16) duplicate the background colour from CSS. If the background colour changes in the stylesheet, this value won't update automatically, creating a maintenance burden.Consider reading the background colour from a CSS custom property or computing it from
getComputedStyle.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/lib/heatmap.ts` around lines 12 - 18, The shade function currently hardcodes the background RGB as { r: 22, g: 27, b: 34 }, which can drift from the actual CSS background; change shade to read the background color dynamically (e.g., from a CSS custom property or via getComputedStyle) instead of the literal object: locate the shade function in web/src/lib/heatmap.ts and replace the hardcoded bg with a value parsed from a CSS variable (e.g., --app-bg) or computed style on a root element, convert that CSS color to r/g/b and use those values in the existing rgb(...) computation so the heatmap always matches the stylesheet.
86-90: ⚡ Quick winConsider extracting weekend highlight colour to theme.
The hardcoded
rgba(240,170,90,0.05)for weekend highlighting should come from the theme system or a CSS custom property rather than being hardcoded, for consistency with the rest of the colour system.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/lib/heatmap.ts` around lines 86 - 90, The weekend highlight RGBA is hardcoded in the heatmap cell generation (inside week.forEach where cellBg is computed); replace the literal "rgba(240,170,90,0.05)" with a theme-driven value (e.g. a CSS custom property like --heatmap-weekend-highlight or the app's theme helper) so the weekend overlay comes from the theme system; update the code that builds cellBg (and any inline style for class "heatmap-cell") to use that CSS variable or theme accessor with a sensible fallback matching the current RGBA.web/src/main.ts (1)
6-14: ⚡ Quick winConsider adding error handling to the bootstrap function.
If
loadData()fails or the app mount throws an exception, the user will see a blank page with no indication of what went wrong. Adding a try-catch block with user-visible error messaging would improve debuggability.🛡️ Suggested error handling
async function bootstrap() { - const data = await loadData(); - mount(App, { - target: document.getElementById("app")!, - props: { data }, - }); + try { + const data = await loadData(); + mount(App, { + target: document.getElementById("app")!, + props: { data }, + }); + } catch (error) { + console.error("Failed to bootstrap application:", error); + const app = document.getElementById("app"); + if (app) { + app.innerHTML = `<div style="padding: 2rem; color: `#ef4444`;"> + <h2>Failed to load dashboard</h2> + <pre>${error}</pre> + </div>`; + } + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/main.ts` around lines 6 - 14, The bootstrap function lacks error handling for failures from loadData() or mount(App,...); wrap the async body of bootstrap in a try-catch, call loadData() and mount(App, ...) inside the try, and in the catch log the error (console.error or a logger) and surface a user-visible message (e.g., render a simple fallback DOM message or use alert) so users see a clear failure instead of a blank page; reference the bootstrap function, loadData, mount, and App when making the change.web/package.json (1)
1-24: ⚡ Quick winConsider adding an
enginesfield to specify required runtime versions.The PR summary mentions Bun 1.3.10 is used in CI and required for contributors touching
web/. Adding anenginesfield would document this requirement and prevent version mismatches.📝 Suggested addition
"private": true, "version": "0.0.0", "type": "module", + "engines": { + "bun": ">=1.3.10" + }, "scripts": {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/package.json` around lines 1 - 24, Add an "engines" field to web/package.json to document and enforce required runtimes (e.g., Bun 1.3.10 used in CI) so contributors get version mismatch warnings; update the top-level JSON object to include an "engines" key with entries like "bun": ">=1.3.10" (and optionally "node"/"npm"/"pnpm" ranges you support) and ensure any CI/docs referring to runtimes align with these version constraints.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 20-22: Replace the tag-based action reference and stale Bun
version in the GitHub Actions step that uses oven-sh/setup-bun: change the
action identifier from "oven-sh/setup-bun@v2" to the pinned commit
"oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6" and update the
bun-version value used in that step from "1.3.10" to "1.3.14" so the CI uses the
fixed action commit and the newer Bun release.
In @.github/workflows/release.yml:
- Around line 16-18: Replace the mutable action reference oven-sh/setup-bun@v2
with the immutable commit pin
oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 in the workflow and
keep or update the bun-version key accordingly; specifically change the action
reference string and, if you want to move to a newer stable Bun, update the
bun-version value from 1.3.10 to the chosen stable release (adjust bun-version
in the same step block where oven-sh/setup-bun is referenced).
In `@web/package.json`:
- Around line 12-23: The package.json currently pins "svelte" at "^5.19.0" while
"`@sveltejs/vite-plugin-svelte`@^5.0.0" expects a peer of "svelte ^5.46.4", so
update the dependency to a compatible range (e.g. change the "svelte" entry in
web/package.json to "^5.46.4" or a range that includes 5.46.4) to align with the
plugin; after bumping, run an install and confirm build with existing Vite
config (notably the transformIndexHtml/enforce: "post" usage) to ensure no
runtime/compiler mismatches. Ensure package-lock or yarn.lock is
updated/committed so the resolved version stays consistent.
In `@web/public/mock-data.json`:
- Line 11: Replace the real personal email "inspiro@gmail.com" used in the mock
JSON with a synthetic address (e.g., "tyom@example.com") everywhere it appears;
search for the JSON key "email" and any occurrences of the value
"inspiro@gmail.com" (including the entries referenced in the diff and the
repeated fixtures) and update them consistently so no real PII remains in the
fixture data.
In `@web/src/lib/dashboard.ts`:
- Around line 19-20: Guard the derived ratios c.lc and c.avgPerDay against
division by zero: when computing c.lc (using c.net / c.commits) and c.avgPerDay
(using c.commits / c.activeDays) check that c.commits and c.activeDays are
non-zero (or truthy) before dividing and use a safe fallback (e.g., 0 or null)
when they are zero/undefined; update the expressions around c.lc and c.avgPerDay
to conditionally compute the toFixed(1) result only when the divisor is valid to
avoid Infinity/NaN in UI output.
In `@web/src/lib/format.ts`:
- Line 5: The pct function can produce Infinity/NaN when t is 0; update the
pct(n: number, t: number) implementation to guard for t === 0 (or near-zero) and
return a sensible default string (e.g., "0.0%") instead of performing the
division, otherwise perform the existing calculation and formatting; locate the
pct function in web/src/lib/format.ts and add the zero-check before computing
((n / t) * 100).toFixed(1) + "%".
In `@web/src/styles/app.css`:
- Line 152: The CSS uses the deprecated declaration "word-break: break-word"
(e.g., in the .timeline-tooltip .tt-subject rule) — find every occurrence of
"word-break: break-word" and replace it with a non-deprecated combination: set
"word-break: normal;" and add "overflow-wrap: anywhere;" so long words still
wrap correctly; update each rule (including .timeline-tooltip .tt-subject and
the other locations flagged) to remove the deprecated property and add these two
properties instead.
---
Nitpick comments:
In `@web/package.json`:
- Around line 1-24: Add an "engines" field to web/package.json to document and
enforce required runtimes (e.g., Bun 1.3.10 used in CI) so contributors get
version mismatch warnings; update the top-level JSON object to include an
"engines" key with entries like "bun": ">=1.3.10" (and optionally
"node"/"npm"/"pnpm" ranges you support) and ensure any CI/docs referring to
runtimes align with these version constraints.
In `@web/src/lib/heatmap.ts`:
- Around line 12-18: The shade function currently hardcodes the background RGB
as { r: 22, g: 27, b: 34 }, which can drift from the actual CSS background;
change shade to read the background color dynamically (e.g., from a CSS custom
property or via getComputedStyle) instead of the literal object: locate the
shade function in web/src/lib/heatmap.ts and replace the hardcoded bg with a
value parsed from a CSS variable (e.g., --app-bg) or computed style on a root
element, convert that CSS color to r/g/b and use those values in the existing
rgb(...) computation so the heatmap always matches the stylesheet.
- Around line 86-90: The weekend highlight RGBA is hardcoded in the heatmap cell
generation (inside week.forEach where cellBg is computed); replace the literal
"rgba(240,170,90,0.05)" with a theme-driven value (e.g. a CSS custom property
like --heatmap-weekend-highlight or the app's theme helper) so the weekend
overlay comes from the theme system; update the code that builds cellBg (and any
inline style for class "heatmap-cell") to use that CSS variable or theme
accessor with a sensible fallback matching the current RGBA.
In `@web/src/main.ts`:
- Around line 6-14: The bootstrap function lacks error handling for failures
from loadData() or mount(App,...); wrap the async body of bootstrap in a
try-catch, call loadData() and mount(App, ...) inside the try, and in the catch
log the error (console.error or a logger) and surface a user-visible message
(e.g., render a simple fallback DOM message or use alert) so users see a clear
failure instead of a blank page; reference the bootstrap function, loadData,
mount, and App when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ec89ac6d-5afc-4993-a5d9-bd4c0bcd4f9e
⛔ Files ignored due to path filters (2)
dist/repo-intelis excluded by!**/dist/**web/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (32)
.githooks/pre-commit.github/workflows/ci.yml.github/workflows/release.yml.gitignoreMakefileREADME.mdbuild.pyrepo-intel.pytemplate.htmlweb/.gitignoreweb/index.htmlweb/package.jsonweb/public/mock-data.jsonweb/src/App.svelteweb/src/data.tsweb/src/lib/avatar.tsweb/src/lib/chart.tsweb/src/lib/charts.tsweb/src/lib/dashboard.tsweb/src/lib/format.tsweb/src/lib/header.tsweb/src/lib/heatmap.tsweb/src/lib/interactions.tsweb/src/lib/popovers.tsweb/src/lib/table.tsweb/src/lib/theme.tsweb/src/lib/timeline.tsweb/src/main.tsweb/src/styles/app.cssweb/src/types.tsweb/tsconfig.jsonweb/vite.config.ts
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.42.2)
web/src/lib/header.ts
[warning] 37-37: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: subtitleEl.innerHTML = ${D.dateRange.start} — ${D.dateRange.end} · ${fmt(totals.commits)} commits · <span style="color:${colorAdded}">+${fmt(totals.added)}</span> <span style="color:${colorDeleted}">-${fmt(totals.deleted)}</span> (net ${subtitleNetHtml}) · ${fmt(totalContribCount)} contributor${totalContribCount === 1 ? "" : "s"}${sizeStr ? · ${sizeStr} : ""}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 58-61: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: langEl.innerHTML = repoLangs.length
? <div class="tech-bar-label">${langLabel}</div> + langBarHtml(repoLangs, { legend: true, repoBase: D.githubBaseUrl })
: <div class="tech-bar-label">Languages</div> +
<div class="tech-empty">Couldn't load language data from the GitHub API for this repo.</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 66-80: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: fwEl.innerHTML =
<div class="tech-bar-label">Frameworks & tools</div> +
(frameworks.length
? <div class="frameworks"> +
frameworks
.map(
(g) =>
<div class="fw-group"> +
<span class="fw-lang"><span class="lang-dot" style="background:${g.color}"></span>${escapeHtml(g.language)}</span> +
<span class="fw-items">${g.names.map(escapeHtml).join(", ")}</span> +
</div>,
)
.join("") +
</div>
: <div class="tech-empty">No known frameworks detected in the repo's dependency manifests.</div>)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 37-37: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: subtitleEl.innerHTML = ${D.dateRange.start} — ${D.dateRange.end} · ${fmt(totals.commits)} commits · <span style="color:${colorAdded}">+${fmt(totals.added)}</span> <span style="color:${colorDeleted}">-${fmt(totals.deleted)}</span> (net ${subtitleNetHtml}) · ${fmt(totalContribCount)} contributor${totalContribCount === 1 ? "" : "s"}${sizeStr ? · ${sizeStr} : ""}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 58-61: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: langEl.innerHTML = repoLangs.length
? <div class="tech-bar-label">${langLabel}</div> + langBarHtml(repoLangs, { legend: true, repoBase: D.githubBaseUrl })
: <div class="tech-bar-label">Languages</div> +
<div class="tech-empty">Couldn't load language data from the GitHub API for this repo.</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 66-80: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: fwEl.innerHTML =
<div class="tech-bar-label">Frameworks & tools</div> +
(frameworks.length
? <div class="frameworks"> +
frameworks
.map(
(g) =>
<div class="fw-group"> +
<span class="fw-lang"><span class="lang-dot" style="background:${g.color}"></span>${escapeHtml(g.language)}</span> +
<span class="fw-items">${g.names.map(escapeHtml).join(", ")}</span> +
</div>,
)
.join("") +
</div>
: <div class="tech-empty">No known frameworks detected in the repo's dependency manifests.</div>)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/heatmap.ts
[warning] 98-98: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: container.innerHTML = <div class="heatmap-wrap">${monthHtml}<div class="heatmap-grid">${dayLabelsHtml}${gridHtml}</div>${legendHtml}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 116-116: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: togglesDiv.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 139-139: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML = <span class="tt-dot" style="background:${cellColorAttr}"></span><span class="tt-date">${label}</span><span class="tt-count">on ${formatted}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 98-98: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: container.innerHTML = <div class="heatmap-wrap">${monthHtml}<div class="heatmap-grid">${dayLabelsHtml}${gridHtml}</div>${legendHtml}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 116-116: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: togglesDiv.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 139-139: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML = <span class="tt-dot" style="background:${cellColorAttr}"></span><span class="tt-date">${label}</span><span class="tt-count">on ${formatted}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/table.ts
[warning] 32-32: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tbody.innerHTML = rowsHtml + subtotalHtml + totalsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 32-32: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tbody.innerHTML = rowsHtml + subtotalHtml + totalsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
web/src/lib/popovers.ts
[warning] 59-68: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: authorPopover.innerHTML =
<div class="lp-header">${avatarHtml}<div class="lp-id"><div class="lp-name">${escapeHtml(c.name)}</div>${handle ? ${escapeHtml(handle)} : ""}</div></div> +
bioHtml +
countsHtml +
metaHtml +
<div class="lp-divider"></div> +
<div class="lp-stats">${fmt(c.commits)} commits · ${c.activeDays} active day${c.activeDays === 1 ? "" : "s"}</div> +
<div class="lp-stats"><span class="add">+${fmt(c.added)}</span> <span class="del">-${fmt(c.deleted)}</span> (net ${netHtml})</div> +
<div class="lp-period">${c.first} — ${c.last}</div> +
langBarHtml(c.languages, { legend: true })
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 165-168: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: commitPopover.innerHTML =
<div class="cp-title" style="color:${clr(colorIdx)}">${escapeHtml(label)}</div> +
<div class="cp-sub">${escapeHtml(c.name)} · ${fmt(list.length)} commit${list.length === 1 ? "" : "s"}</div> +
<div class="cp-list">${rows}</div>${more}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 59-68: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: authorPopover.innerHTML =
<div class="lp-header">${avatarHtml}<div class="lp-id"><div class="lp-name">${escapeHtml(c.name)}</div>${handle ? ${escapeHtml(handle)} : ""}</div></div> +
bioHtml +
countsHtml +
metaHtml +
<div class="lp-divider"></div> +
<div class="lp-stats">${fmt(c.commits)} commits · ${c.activeDays} active day${c.activeDays === 1 ? "" : "s"}</div> +
<div class="lp-stats"><span class="add">+${fmt(c.added)}</span> <span class="del">-${fmt(c.deleted)}</span> (net ${netHtml})</div> +
<div class="lp-period">${c.first} — ${c.last}</div> +
langBarHtml(c.languages, { legend: true })
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 165-168: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: commitPopover.innerHTML =
<div class="cp-title" style="color:${clr(colorIdx)}">${escapeHtml(label)}</div> +
<div class="cp-sub">${escapeHtml(c.name)} · ${fmt(list.length)} commit${list.length === 1 ? "" : "s"}</div> +
<div class="cp-list">${rows}</div>${more}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/charts.ts
[warning] 114-114: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: card.innerHTML = <div class="rank">#${i + 1}</div><div class="name" style="color:${clr(i)}">${escapeHtml(c.name)}</div><div class="meta"><span>${fmt(c.commits)} commits</span><span class="add">${fmt(c.added)} ++</span><span class="del">${fmt(c.deleted)} --</span></div><canvas id="contrib-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 155-155: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: div.innerHTML = <canvas id="${idPrefix}-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 114-114: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: card.innerHTML = <div class="rank">#${i + 1}</div><div class="name" style="color:${clr(i)}">${escapeHtml(c.name)}</div><div class="meta"><span>${fmt(c.commits)} commits</span><span class="add">${fmt(c.added)} ++</span><span class="del">${fmt(c.deleted)} --</span></div><canvas id="contrib-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 155-155: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: div.innerHTML = <canvas id="${idPrefix}-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/timeline.ts
[warning] 84-84: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: labelsDiv.innerHTML = labelsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 426-426: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: yearsBar.innerHTML = buildYearsInner(currentWidth)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 437-437: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: oldAxis.outerHTML = newAxis
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 923-926: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML =
<div class="tt-author-row">${avatarHtml}<span class="tt-author">${escapeHtml(author.name)}</span>${headerExtra}</div> +
body +
ftypesHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 1202-1205: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML =
<div class="tt-author-row"><span class="tt-tag-icon"></span><span class="tt-tag-kicker">TAG</span><span class="tt-tag-name">${escapeHtml(t.name || "")}</span></div> +
(showMsg ? <div class="tt-subject">${escapeHtml(msg)}</div> : "") +
<div class="tt-meta">${escapeHtml(dateStr || t.date || "")}${timeStr ? " " + escapeHtml(timeStr) : ""}${oidShort ? ' · <span class="tt-hash">' + escapeHtml(oidShort) + "</span>" : ""}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 84-84: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: labelsDiv.innerHTML = labelsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 426-426: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: yearsBar.innerHTML = buildYearsInner(currentWidth)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 437-437: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: oldAxis.outerHTML = newAxis
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 923-926: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML =
<div class="tt-author-row">${avatarHtml}<span class="tt-author">${escapeHtml(author.name)}</span>${headerExtra}</div> +
body +
ftypesHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 1202-1205: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML =
<div class="tt-author-row"><span class="tt-tag-icon"></span><span class="tt-tag-kicker">TAG</span><span class="tt-tag-name">${escapeHtml(t.name || "")}</span></div> +
(showMsg ? <div class="tt-subject">${escapeHtml(msg)}</div> : "") +
<div class="tt-meta">${escapeHtml(dateStr || t.date || "")}${timeStr ? " " + escapeHtml(timeStr) : ""}${oidShort ? ' · <span class="tt-hash">' + escapeHtml(oidShort) + "</span>" : ""}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
🪛 OpenGrep (1.21.0)
web/src/lib/format.ts
[ERROR] 46-46: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
[ERROR] 47-47: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
[ERROR] 83-83: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
web/src/lib/popovers.ts
[ERROR] 105-105: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🪛 Stylelint (17.11.1)
web/src/styles/app.css
[error] 152-152: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 159-159: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 181-181: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 183-183: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
🪛 zizmor (1.25.2)
.github/workflows/ci.yml
[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
.github/workflows/release.yml
[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default
(cache-poisoning)
🔇 Additional comments (26)
web/src/lib/format.ts (1)
1-4: LGTM!Also applies to: 7-116
web/src/lib/chart.ts (1)
1-41: LGTM!web/src/lib/charts.ts (1)
1-250: LGTM!web/src/lib/timeline.ts (1)
1-1332: LGTM!web/src/lib/table.ts (1)
1-47: LGTM!web/src/lib/popovers.ts (1)
1-210: LGTM!web/src/lib/interactions.ts (1)
1-128: LGTM!web/src/types.ts (1)
1-105: LGTM!web/src/lib/avatar.ts (1)
1-16: LGTM!web/src/data.ts (1)
1-16: LGTM!web/src/App.svelte (1)
1-121: LGTM!web/src/lib/theme.ts (1)
1-28: LGTM!web/src/lib/header.ts (2)
7-46: LGTM!
49-83: LGTM!web/src/lib/heatmap.ts (1)
20-100: LGTM!Also applies to: 105-126, 128-170
.githooks/pre-commit (1)
17-17: LGTM!Also applies to: 40-43
.gitignore (1)
1-8: LGTM!Makefile (1)
6-10: LGTM!Also applies to: 12-12, 18-18, 25-25
README.md (1)
209-245: LGTM!build.py (1)
26-32: LGTM!Also applies to: 43-43
repo-intel.py (1)
1935-1940: LGTM!web/.gitignore (1)
1-2: LGTM!web/index.html (1)
1-15: LGTM!web/tsconfig.json (1)
1-16: LGTM!web/vite.config.ts (1)
12-24: ChecktransformIndexHtmlordering semantics in Vite 6 (enforcevs hookorder) inweb/vite.config.ts(lines 12–24).
transformIndexHtml: { order: "post" }and plugin-levelenforce: "post"are both valid and control different execution ordering dimensions in Vite 6; however, Vite 6viteSingleFile()can also affect HTML transforms—confirm your plugin’s execution is afterviteSingleFile()based on howviteSingleFile()is configured/ordered in thepluginsarray.web/src/main.ts (1)
2-2: Svelte 5.19 mount API usage matches the documented signature —web/src/main.tsimportsmountfrom"svelte"and callsmount(App, { target: document.getElementById("app")!, props: { data } }), which aligns with Svelte 5’smount(component, { target, props, ... })signature.
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.githooks/pre-commit (1)
26-32:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle staged filenames safely in the unstaged-check loop (Line 26).
for f in $stagedis whitespace-splitting and can mis-handle valid filenames. Use NUL-delimited output from git and read safely.Proposed fix
-for f in $staged; do - if ! git diff --quiet -- "$f"; then - echo "pre-commit: '$f' has unstaged changes; the rebuild would embed them." >&2 - echo " Stage them (git add $f) or stash, then commit again." >&2 - exit 1 - fi -done +git diff --cached --name-only -z -- $sources | while IFS= read -r -d '' f; do + if ! git diff --quiet -- "$f"; then + echo "pre-commit: '$f' has unstaged changes; the rebuild would embed them." >&2 + echo " Stage them (git add \"$f\") or stash, then commit again." >&2 + exit 1 + fi +done🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.githooks/pre-commit around lines 26 - 32, The loop using "for f in $staged" splits filenames on whitespace and breaks on names with spaces/newlines; change to use NUL-delimited git output and a safe read loop: produce staged with git (e.g., git diff --name-only --cached -z) and iterate with while IFS= read -r -d '' f; do ... done, keeping the existing git diff --quiet -- "$f" check and preserving the error messages but quoting "$f" where used.
♻️ Duplicate comments (3)
.github/workflows/ci.yml (1)
20-22:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse an immutable commit pin for
oven-sh/setup-bun.Line 20 uses a mutable major tag. For CI supply-chain hardening and reproducibility, pin this action to a commit SHA.
#!/bin/bash set -euo pipefail echo "Current ci.yml reference:" awk 'NR>=18 && NR<=24 {printf "%5d %s\n", NR, $0}' .github/workflows/ci.yml echo echo "Resolve oven-sh/setup-bun tag v2 to its current commit SHA:" curl -sS -H "Accept: application/vnd.github+json" \ https://api.github.com/repos/oven-sh/setup-bun/git/ref/tags/v2 \ | jq -r '.object.sha'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 20 - 22, The workflow is using the mutable tag "oven-sh/setup-bun@v2" which is not pinned to a commit SHA; replace the action reference with the resolved immutable commit SHA (e.g., oven-sh/setup-bun@<full-commit-sha>) to harden CI and ensure reproducible runs. Locate the usage of the action string "oven-sh/setup-bun@v2" in the CI workflow and update it to the corresponding commit SHA returned by the GitHub API (use the tag v2 to find its object.sha) so the workflow references the exact commit instead of a floating tag. Ensure the bun-version input remains unchanged..github/workflows/release.yml (1)
16-18:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPin
oven-sh/setup-bunto an immutable commit SHA.Line 16 currently tracks a mutable tag (
@v2), which weakens release-chain integrity. Please pin to a full commit digest.#!/bin/bash set -euo pipefail echo "Current release.yml reference:" awk 'NR>=14 && NR<=20 {printf "%5d %s\n", NR, $0}' .github/workflows/release.yml echo echo "Resolve oven-sh/setup-bun tag v2 to its current commit SHA:" curl -sS -H "Accept: application/vnd.github+json" \ https://api.github.com/repos/oven-sh/setup-bun/git/ref/tags/v2 \ | jq -r '.object.sha'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yml around lines 16 - 18, Replace the mutable tag for the GitHub Action in your workflow: change the uses entry from "oven-sh/setup-bun@v2" to the same action pinned to the full commit SHA (e.g. "oven-sh/setup-bun@<full-commit-sha>"). Resolve the v2 tag to its current commit SHA using the GitHub API (or `git ls-remote`), then update the release workflow's uses line with that full SHA to ensure the action is immutable and repeatable.web/src/lib/dashboard.ts (1)
19-20:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard ratio calculations against zero divisors.
Line 19 and Line 20 still allow
Infinity/NaNwhencommitsoractiveDaysis0. Please keep these derived values bounded before formatting.Proposed fix
- c.lc = +(c.net / c.commits).toFixed(1); - c.avgPerDay = +(c.commits / c.activeDays).toFixed(1); + c.lc = c.commits > 0 ? +(c.net / c.commits).toFixed(1) : 0; + c.avgPerDay = c.activeDays > 0 ? +(c.commits / c.activeDays).toFixed(1) : 0;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/lib/dashboard.ts` around lines 19 - 20, The ratio calculations for c.lc and c.avgPerDay can produce Infinity/NaN when c.commits or c.activeDays is 0; update the logic that sets c.lc and c.avgPerDay to first check the divisor (c.commits and c.activeDays respectively) and only perform the division and .toFixed(1) when the divisor is truthy/non-zero, otherwise assign a safe fallback (e.g., 0 or null) so the values are bounded before formatting; locate and change the lines that set c.lc and c.avgPerDay in the dashboard code to implement these guards.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@build.py`:
- Around line 24-25: The file reads/writes text using
Path.read_text()/write_text() without an explicit encoding which can produce
non-deterministic results; update all usages (e.g., where you assign script =
(src_dir / "repo-intel.py").read_text(), and the other occurrences at the
locations noted) to call read_text(encoding="utf-8") and write_text(...,
encoding="utf-8") so all text IO is explicitly UTF-8 encoded for deterministic
cross-platform bundling.
In `@web/package.json`:
- Around line 13-16: The svelte dependency declared as "svelte": "^5.19.0" is
incompatible with `@sveltejs/vite-plugin-svelte`@^5.0.0's peer requirement; update
the "svelte" entry to "^5.46.4" (or newer) in package.json so it satisfies
`@sveltejs/vite-plugin-svelte`, then reinstall and regenerate your lockfile
(npm/yarn/pnpm) to pin the resolved version; verify the package.json entry for
"svelte" and the lockfile reflect the new version and run a build to confirm no
peer conflicts.
In `@web/src/lib/header.ts`:
- Around line 73-76: The template injects g.color directly into an inline style
in the header renderer (the code block that builds the `<span class="fw-lang">`
with `style="background:${g.color}"`), which is unsafe; add a strict validator
function (e.g., isValidHexRgbHslColor) and use it to gate g.color before
interpolation—if validation fails, substitute a safe default color or omit the
style attribute entirely; update the code that builds the string (the mapping
that references g.color) to call the validator and only include `background:`
when valid.
In `@web/src/lib/popovers.ts`:
- Line 68: The template string that assigns to innerHTML is inserting c.first
and c.last raw (the line with `<div class="lp-period">${c.first} —
${c.last}</div>`); escape both values before concatenation to prevent HTML
injection by creating/using an HTML-escaping helper (e.g., escapeHtml) and
replace `${c.first}` and `${c.last}` with escaped versions when building the
innerHTML string so only safe text is injected.
- Line 159: Construct the commit URL into a local variable first (e.g., const
commitUrl = `${D.githubBaseUrl}/commit/${encodeURIComponent(c2.h)}`) and then
HTML-escape that variable before interpolating into the template string for the
anchor href; update the code around the template literal that currently uses
`${D.githubBaseUrl}/commit/${encodeURIComponent(c2.h)}` to use the escaped value
(e.g., escapedCommitUrl). If there is a shared utility (like an existing
escapeHtml/htmlEscape), reuse it; otherwise add a small escapeHtml function that
replaces & < > " ' with entities and call it when building the href in the
popovers.ts function that renders the commit row.
In `@web/src/lib/table.ts`:
- Line 28: The href attribute is being injected directly from authorUrl(D, c)
which can lead to attribute injection; update the template in the function that
returns the table row so the URL is safely escaped/encoded before insertion
(e.g. replace href="${authorUrl(D, c)}" with href="${escapeHtml(authorUrl(D,
c))}" or use a URL-encoding helper like encodeURI/encodeURIComponent on
authorUrl's output), ensuring you call the existing escape/encoding utility
(escapeHtml or equivalent) where the template is constructed so authorUrl(D, c)
is sanitized before rendering.
- Line 32: The totals row currently divides totals.net by totals.commits in the
expression +(totals.net! / totals.commits).toFixed(1) which can yield
Infinity/NaN when totals.commits is zero; update the construction of totalsHtml
in web/src/lib/table.ts to guard that division by checking totals.commits (or
totals.commits === 0) and return a safe fallback (e.g., "0" or "-" as a string
or 0 as a number formatted via fmt) when commits is zero, otherwise perform the
division and toFixed(1); adjust the expression inside the template literal so
the fallback is used instead of the raw division.
---
Outside diff comments:
In @.githooks/pre-commit:
- Around line 26-32: The loop using "for f in $staged" splits filenames on
whitespace and breaks on names with spaces/newlines; change to use NUL-delimited
git output and a safe read loop: produce staged with git (e.g., git diff
--name-only --cached -z) and iterate with while IFS= read -r -d '' f; do ...
done, keeping the existing git diff --quiet -- "$f" check and preserving the
error messages but quoting "$f" where used.
---
Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 20-22: The workflow is using the mutable tag
"oven-sh/setup-bun@v2" which is not pinned to a commit SHA; replace the action
reference with the resolved immutable commit SHA (e.g.,
oven-sh/setup-bun@<full-commit-sha>) to harden CI and ensure reproducible runs.
Locate the usage of the action string "oven-sh/setup-bun@v2" in the CI workflow
and update it to the corresponding commit SHA returned by the GitHub API (use
the tag v2 to find its object.sha) so the workflow references the exact commit
instead of a floating tag. Ensure the bun-version input remains unchanged.
In @.github/workflows/release.yml:
- Around line 16-18: Replace the mutable tag for the GitHub Action in your
workflow: change the uses entry from "oven-sh/setup-bun@v2" to the same action
pinned to the full commit SHA (e.g. "oven-sh/setup-bun@<full-commit-sha>").
Resolve the v2 tag to its current commit SHA using the GitHub API (or `git
ls-remote`), then update the release workflow's uses line with that full SHA to
ensure the action is immutable and repeatable.
In `@web/src/lib/dashboard.ts`:
- Around line 19-20: The ratio calculations for c.lc and c.avgPerDay can produce
Infinity/NaN when c.commits or c.activeDays is 0; update the logic that sets
c.lc and c.avgPerDay to first check the divisor (c.commits and c.activeDays
respectively) and only perform the division and .toFixed(1) when the divisor is
truthy/non-zero, otherwise assign a safe fallback (e.g., 0 or null) so the
values are bounded before formatting; locate and change the lines that set c.lc
and c.avgPerDay in the dashboard code to implement these guards.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6f1d071b-20a7-49ca-b7c3-9656b20d6e70
⛔ Files ignored due to path filters (2)
dist/repo-intelis excluded by!**/dist/**web/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (32)
.githooks/pre-commit.github/workflows/ci.yml.github/workflows/release.yml.gitignoreMakefileREADME.mdbuild.pyrepo-intel.pytemplate.htmlweb/.gitignoreweb/index.htmlweb/package.jsonweb/public/mock-data.jsonweb/src/App.svelteweb/src/data.tsweb/src/lib/avatar.tsweb/src/lib/chart.tsweb/src/lib/charts.tsweb/src/lib/dashboard.tsweb/src/lib/format.tsweb/src/lib/header.tsweb/src/lib/heatmap.tsweb/src/lib/interactions.tsweb/src/lib/popovers.tsweb/src/lib/table.tsweb/src/lib/theme.tsweb/src/lib/timeline.tsweb/src/main.tsweb/src/styles/app.cssweb/src/types.tsweb/tsconfig.jsonweb/vite.config.ts
✅ Files skipped from review due to trivial changes (5)
- web/.gitignore
- web/index.html
- repo-intel.py
- .gitignore
- README.md
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.42.2)
web/src/lib/header.ts
[warning] 37-37: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: subtitleEl.innerHTML = ${D.dateRange.start} — ${D.dateRange.end} · ${fmt(totals.commits)} commits · <span style="color:${colorAdded}">+${fmt(totals.added)}</span> <span style="color:${colorDeleted}">-${fmt(totals.deleted)}</span> (net ${subtitleNetHtml}) · ${fmt(totalContribCount)} contributor${totalContribCount === 1 ? "" : "s"}${sizeStr ? · ${sizeStr} : ""}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 58-61: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: langEl.innerHTML = repoLangs.length
? <div class="tech-bar-label">${langLabel}</div> + langBarHtml(repoLangs, { legend: true, repoBase: D.githubBaseUrl })
: <div class="tech-bar-label">Languages</div> +
<div class="tech-empty">Couldn't load language data from the GitHub API for this repo.</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 66-80: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: fwEl.innerHTML =
<div class="tech-bar-label">Frameworks & tools</div> +
(frameworks.length
? <div class="frameworks"> +
frameworks
.map(
(g) =>
<div class="fw-group"> +
<span class="fw-lang"><span class="lang-dot" style="background:${g.color}"></span>${escapeHtml(g.language)}</span> +
<span class="fw-items">${g.names.map(escapeHtml).join(", ")}</span> +
</div>,
)
.join("") +
</div>
: <div class="tech-empty">No known frameworks detected in the repo's dependency manifests.</div>)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 37-37: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: subtitleEl.innerHTML = ${D.dateRange.start} — ${D.dateRange.end} · ${fmt(totals.commits)} commits · <span style="color:${colorAdded}">+${fmt(totals.added)}</span> <span style="color:${colorDeleted}">-${fmt(totals.deleted)}</span> (net ${subtitleNetHtml}) · ${fmt(totalContribCount)} contributor${totalContribCount === 1 ? "" : "s"}${sizeStr ? · ${sizeStr} : ""}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 58-61: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: langEl.innerHTML = repoLangs.length
? <div class="tech-bar-label">${langLabel}</div> + langBarHtml(repoLangs, { legend: true, repoBase: D.githubBaseUrl })
: <div class="tech-bar-label">Languages</div> +
<div class="tech-empty">Couldn't load language data from the GitHub API for this repo.</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 66-80: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: fwEl.innerHTML =
<div class="tech-bar-label">Frameworks & tools</div> +
(frameworks.length
? <div class="frameworks"> +
frameworks
.map(
(g) =>
<div class="fw-group"> +
<span class="fw-lang"><span class="lang-dot" style="background:${g.color}"></span>${escapeHtml(g.language)}</span> +
<span class="fw-items">${g.names.map(escapeHtml).join(", ")}</span> +
</div>,
)
.join("") +
</div>
: <div class="tech-empty">No known frameworks detected in the repo's dependency manifests.</div>)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/popovers.ts
[warning] 59-68: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: authorPopover.innerHTML =
<div class="lp-header">${avatarHtml}<div class="lp-id"><div class="lp-name">${escapeHtml(c.name)}</div>${handle ? ${escapeHtml(handle)} : ""}</div></div> +
bioHtml +
countsHtml +
metaHtml +
<div class="lp-divider"></div> +
<div class="lp-stats">${fmt(c.commits)} commits · ${c.activeDays} active day${c.activeDays === 1 ? "" : "s"}</div> +
<div class="lp-stats"><span class="add">+${fmt(c.added)}</span> <span class="del">-${fmt(c.deleted)}</span> (net ${netHtml})</div> +
<div class="lp-period">${c.first} — ${c.last}</div> +
langBarHtml(c.languages, { legend: true })
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 165-168: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: commitPopover.innerHTML =
<div class="cp-title" style="color:${clr(colorIdx)}">${escapeHtml(label)}</div> +
<div class="cp-sub">${escapeHtml(c.name)} · ${fmt(list.length)} commit${list.length === 1 ? "" : "s"}</div> +
<div class="cp-list">${rows}</div>${more}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 59-68: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: authorPopover.innerHTML =
<div class="lp-header">${avatarHtml}<div class="lp-id"><div class="lp-name">${escapeHtml(c.name)}</div>${handle ? ${escapeHtml(handle)} : ""}</div></div> +
bioHtml +
countsHtml +
metaHtml +
<div class="lp-divider"></div> +
<div class="lp-stats">${fmt(c.commits)} commits · ${c.activeDays} active day${c.activeDays === 1 ? "" : "s"}</div> +
<div class="lp-stats"><span class="add">+${fmt(c.added)}</span> <span class="del">-${fmt(c.deleted)}</span> (net ${netHtml})</div> +
<div class="lp-period">${c.first} — ${c.last}</div> +
langBarHtml(c.languages, { legend: true })
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 165-168: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: commitPopover.innerHTML =
<div class="cp-title" style="color:${clr(colorIdx)}">${escapeHtml(label)}</div> +
<div class="cp-sub">${escapeHtml(c.name)} · ${fmt(list.length)} commit${list.length === 1 ? "" : "s"}</div> +
<div class="cp-list">${rows}</div>${more}
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
web/src/lib/charts.ts
[warning] 114-114: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: card.innerHTML = <div class="rank">#${i + 1}</div><div class="name" style="color:${clr(i)}">${escapeHtml(c.name)}</div><div class="meta"><span>${fmt(c.commits)} commits</span><span class="add">${fmt(c.added)} ++</span><span class="del">${fmt(c.deleted)} --</span></div><canvas id="contrib-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 155-155: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: div.innerHTML = <canvas id="${idPrefix}-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 114-114: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: card.innerHTML = <div class="rank">#${i + 1}</div><div class="name" style="color:${clr(i)}">${escapeHtml(c.name)}</div><div class="meta"><span>${fmt(c.commits)} commits</span><span class="add">${fmt(c.added)} ++</span><span class="del">${fmt(c.deleted)} --</span></div><canvas id="contrib-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 155-155: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: div.innerHTML = <canvas id="${idPrefix}-${i}"></canvas>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
web/src/lib/table.ts
[warning] 32-32: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tbody.innerHTML = rowsHtml + subtotalHtml + totalsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 32-32: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tbody.innerHTML = rowsHtml + subtotalHtml + totalsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
web/src/lib/heatmap.ts
[warning] 98-98: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: container.innerHTML = <div class="heatmap-wrap">${monthHtml}<div class="heatmap-grid">${dayLabelsHtml}${gridHtml}</div>${legendHtml}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 116-116: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: togglesDiv.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 139-139: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML = <span class="tt-dot" style="background:${cellColorAttr}"></span><span class="tt-date">${label}</span><span class="tt-count">on ${formatted}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 98-98: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: container.innerHTML = <div class="heatmap-wrap">${monthHtml}<div class="heatmap-grid">${dayLabelsHtml}${gridHtml}</div>${legendHtml}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 116-116: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: togglesDiv.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 139-139: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML = <span class="tt-dot" style="background:${cellColorAttr}"></span><span class="tt-date">${label}</span><span class="tt-count">on ${formatted}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
web/src/lib/timeline.ts
[warning] 84-84: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: labelsDiv.innerHTML = labelsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 426-426: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: yearsBar.innerHTML = buildYearsInner(currentWidth)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 437-437: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: oldAxis.outerHTML = newAxis
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 923-926: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML =
<div class="tt-author-row">${avatarHtml}<span class="tt-author">${escapeHtml(author.name)}</span>${headerExtra}</div> +
body +
ftypesHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 1202-1205: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: tooltip.innerHTML =
<div class="tt-author-row"><span class="tt-tag-icon"></span><span class="tt-tag-kicker">TAG</span><span class="tt-tag-name">${escapeHtml(t.name || "")}</span></div> +
(showMsg ? <div class="tt-subject">${escapeHtml(msg)}</div> : "") +
<div class="tt-meta">${escapeHtml(dateStr || t.date || "")}${timeStr ? " " + escapeHtml(timeStr) : ""}${oidShort ? ' · <span class="tt-hash">' + escapeHtml(oidShort) + "</span>" : ""}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 84-84: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: labelsDiv.innerHTML = labelsHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 426-426: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: yearsBar.innerHTML = buildYearsInner(currentWidth)
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 437-437: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: oldAxis.outerHTML = newAxis
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 923-926: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML =
<div class="tt-author-row">${avatarHtml}<span class="tt-author">${escapeHtml(author.name)}</span>${headerExtra}</div> +
body +
ftypesHtml
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 1202-1205: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: tooltip.innerHTML =
<div class="tt-author-row"><span class="tt-tag-icon"></span><span class="tt-tag-kicker">TAG</span><span class="tt-tag-name">${escapeHtml(t.name || "")}</span></div> +
(showMsg ? <div class="tt-subject">${escapeHtml(msg)}</div> : "") +
<div class="tt-meta">${escapeHtml(dateStr || t.date || "")}${timeStr ? " " + escapeHtml(timeStr) : ""}${oidShort ? ' · <span class="tt-hash">' + escapeHtml(oidShort) + "</span>" : ""}</div>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
🪛 OpenGrep (1.21.0)
web/src/lib/format.ts
[ERROR] 46-46: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
[ERROR] 47-47: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
[ERROR] 83-83: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
web/src/lib/popovers.ts
[ERROR] 105-105: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🪛 Stylelint (17.11.1)
web/src/styles/app.css
[error] 152-152: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 159-159: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 181-181: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
[error] 183-183: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
🪛 zizmor (1.25.2)
.github/workflows/release.yml
[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default
(cache-poisoning)
.github/workflows/ci.yml
[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🔇 Additional comments (17)
web/src/data.ts (1)
6-15: LGTM!web/tsconfig.json (1)
1-16: LGTM!web/src/lib/theme.ts (1)
5-27: LGTM!web/src/App.svelte (1)
1-120: LGTM!web/src/lib/chart.ts (1)
7-40: LGTM!web/vite.config.ts (1)
12-28: LGTM!web/src/main.ts (1)
6-14: LGTM!web/src/lib/avatar.ts (1)
5-15: LGTM!web/public/mock-data.json (1)
10-86: LGTM!Makefile (1)
6-25: LGTM!web/src/lib/format.ts (1)
5-5:pctstill needs a zero-denominator guard (Line 5).This is still the same unresolved divide-by-zero path previously reported (
Infinity%/NaN%output whent === 0).web/src/styles/app.css (1)
152-152: Deprecatedword-break: break-wordusage remains in multiple rules.This is the same unresolved deprecation already reported for these selectors.
Also applies to: 159-159, 181-181, 183-183
web/src/lib/interactions.ts (1)
4-67: LGTM!Also applies to: 69-127
web/src/types.ts (1)
1-105: LGTM!web/src/lib/charts.ts (1)
14-250: LGTM!web/src/lib/heatmap.ts (1)
1-171: LGTM!web/src/lib/timeline.ts (1)
1-1332: LGTM!
- format.ts: pct() returns 0.0% instead of NaN% when total is 0 (a repo with no deletions or net==0 hits this in the contributor table) - app.css: replace deprecated `word-break: break-word` with `word-break: normal; overflow-wrap: anywhere` (4 rules) - main.ts: wrap bootstrap() in try/catch so a data-load failure shows a message instead of a blank page - rebuild dist/repo-intel
….py text IO to UTF-8
Replace the imperative renderTable() (innerHTML string-building + event
delegation) with lib/components/Table.svelte:
- {#each} rows with $derived subtotals/totals; names auto-escaped by Svelte
(drops the hand-rolled escapeHtml calls)
- per-row onmouseenter/onmouseleave wiring instead of tbody event delegation
+ data-idx + getElementById; native mouseleave removes the relatedTarget
flicker guard
- author popover lifted to App.svelte and passed into both Table and
initDashboard, so both halves share the one body-appended singleton
table.ts keeps only topCommitSubtotal (still used by charts.ts).
Replace renderHeader() (getElementById + innerHTML + createElement) with
lib/components/Header.svelte:
- title renders as a GitHub link or plain text via {#if}; document.title set
in an $effect
- subtitle stat line built in markup with the colored +/- spans; net derived
locally instead of read from initDashboard's mutation
- timeline heading's ": <duration>" suffix moved to App.svelte (derived from
fmtTimelineDuration), dropping the .timeline-h querySelector
header.ts now only renders the Technologies section (renderTech); its theme /
fmt / fmtSize / fmtTimelineDuration imports are gone.
Replace renderTech() (getElementById + innerHTML + the hidden/reveal dance)
with lib/components/TechGrid.svelte plus a reusable LangBar.svelte:
- TechGrid renders the languages-by-size/churn label, the stacked LangBar, and
the framework groups via {#each}; framework/language names auto-escaped
- LangBar is the component form of langBarHtml(): stacked segments + optional
legend with per-language GitHub code-search links
- the section always renders (with fallbacks for missing GitHub data), so the
initial `hidden` attribute and the reveal logic are gone
langBarHtml() stays in format.ts for now — the still-imperative author popover
renders it inline; it can go once the popover is a component. header.ts is
fully converted and deleted; dashboard.ts no longer references it.
initHeatmap now returns a rebuildHeatmap(mode) closure instead of
wiring the toggles itself; initDashboard threads it out via a
DashboardBridges object. The new YearToggles.svelte owns the year list
and active state (class:active, per-button onclick), and App passes
rebuildHeatmap as its onSelect callback. Drops the buildYearToggles
IIFE, its getElementById('yearToggles'), and the data-mode delegation.
Each card's rank/name/meta markup becomes ContributorCard.svelte, with its weekly-commits sparkline staying imperative on a bind:this canvas created in onMount (and destroyed on unmount). App renders one per contributor in the grid-5; the #contributorCards id and the innerHTML/escapeHtml/getElementById block in charts.ts are gone. Since child onMount fires before parent onMount, configureCharts() moves to App's top-level script so Chart.js defaults are themed before any card sparkline is built. Drops now-unused fmt/escapeHtml imports from charts.ts and the configureCharts call from initDashboard.
The two body-portaled popovers become AuthorPopover.svelte and
CommitPopover.svelte, rendering auto-escaped markup (no more
innerHTML/escapeHtml) and reusing LangBar for the language bar. Their
imperative DOM behaviors are expressed as element directives: use:portal
relocates the node to <body> (so position:fixed isn't trapped by a
transform ancestor) and use:position measures-then-places while owning
the .visible class (no stale-position flash, no flushSync). The avatar
fallback is now an inline onerror + $state flag, reset on contributor
change so a reused <img> node can't strand the next avatar.
State lives in popover-store.svelte.ts ($state); popovers.ts is now thin
adapters writing that store, keeping the createAuthorPopover/
createCommitPopover API and {show,hide,commitsInBucket,dowFull} shape so
timeline.ts and charts.ts call sites are unchanged. langBarHtml is
deleted from format.ts (its last consumer is gone); escapeHtml stays
(still used by the imperative timeline) and avatar.ts stays (timeline's
hover tooltip still uses installAvatarFallback).
Port buildHeatmap() out of the imperative lib/heatmap.ts into
Heatmap.svelte: the calendar grid is now reactive markup ($derived.by
on {mode}) instead of an innerHTML string, and the hover tooltip is a
single body-portaled node positioned with the shared portal/position
actions (like the popovers) rather than a hand-rolled body-appended div
with mouseover/mouseout delegation.
- {mode} drives the grid rebuild reactively, so the year toggles no
longer need the imperative rebuild closure. App owns heatmapMode state
and YearToggles sets it; the DashboardBridges/rebuildHeatmap bridge and
the allDaily aggregation are gone (the component derives allDaily from
data.dailyData itself).
- Cells are auto-escaped Svelte markup with per-cell mouseenter/mouseleave
(guarded against out-of-order dispatch) instead of data-* attributes +
event delegation. Added aria-label per cell (an a11y win over the old
empty <a> squares).
- Mode type moved to types.ts (the only thing left in heatmap.ts after
gutting it); lib/heatmap.ts deleted.
svelte-check clean; dist rebuilt. Verified in-browser: year toggle
rebuilds the grid (Current 52-week window ↔ full calendar year), hover
tooltip shows correct date/count/color and flips side, hides on leave.
Define three source aliases, mirrored in vite.config.ts (resolve.alias) and tsconfig.json (paths) so both the bundler and svelte-check resolve them: $types -> src/types.ts $lib/* -> src/lib/* $components/* -> src/lib/components/* Rewrite the cross-directory relative imports to use them, e.g. `../../types` -> `$types`, `../theme` -> `$lib/theme`, and App's `./lib/components/X.svelte` -> `$components/X.svelte`. Same-directory sibling imports (`./timeline`, `./LangBar.svelte`) are left as-is since they're already clean. Source-only change: the emitted bundle is byte-identical (same JS/CSS hashes, unchanged dist/repo-intel). svelte-check clean.
Move component-exclusive CSS out of the global app.css into each Svelte component's <style> block (multiline + nested): YearToggles, Heatmap, AuthorPopover, CommitPopover, ContributorCard, LangBar, TechGrid. Cross- component LangBar overrides live in AuthorPopover via :global(). Layout, typography, scrollbars, and imperatively-rendered .timeline-*/.chart-* stay global. Consolidate repeated colour literals into :root vars (--bg-popover, --bg-tooltip, --grid-line, --selection-fill/stroke, --accent-weekend) and fix hex values that duplicated existing vars. Canvas/JS code reads the new vars through theme.ts; Heatmap shade() now interpolates from --bg-card. Also restore the heatmap cell hover outline: the selector was gated on .heatmap-cell[data-date], which the migrated <a class="heatmap-cell"> markup never sets, so it had been dead since the Svelte migration. Rebuild dist/repo-intel.
…ions Finishes the imperative→Svelte migration for everything except the hand-drawn canvas timeline: - The four "Overall" charts and the hour/dow pattern cards move from the getElementById + innerHTML rendering in charts.ts into OverallCharts.svelte and PatternCard.svelte. Each owns its canvas via bind:this and destroys its chart on unmount (the imperative version never tore them down). The reset affordance is now declarative (class:has-hidden) instead of a DOM-built button. - initScrollRows / initSidebar become the dragScroll / scrollSpy Svelte actions in actions.ts — scoped per-element with proper teardown. With those gone, the imperative "dashboard engine" collapses: dashboard.ts and charts.ts are deleted and App.svelte calls buildTimeline directly in onMount. Also drops the dead prop-mutation that wrote net/lc/avgPerDay onto the data object: Table.svelte already derives those locally and the pie/ratio charts derive them inline, so the mutation, the optional computed fields in types.ts, and the one-line table.ts helper are removed.
Add Prettier as repo-root dev tooling so the editor and CI agree on style instead of drifting: - Root package.json (prettier + prettier-plugin-svelte) with format / format:check scripts, a .prettierrc (printWidth 100, matching the bulk of the frontend), and a .prettierignore for deps, build output, and generated data. - `make format` / `make format-check`; CI runs format-check before the type-check so style drift fails fast. - One-time reformat of the existing tree to the new config. CSS, markdown, and a few components shift the most since they weren't previously Prettier-clean. - Drop web/.gitignore (the root .gitignore already covers web/node_modules and web/dist) and ignore the root /node_modules used by the tooling. dist/repo-intel is rebuilt here because web/index.html (a build input) was reformatted; the inlined JS/CSS bundles are byte-identical.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/lib/timeline.ts (1)
922-927:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse safe
window.openflags for external navigation.Both click handlers open external pages with
"_blank"only. Addnoopener,noreferrerto prevent opener access.Suggested fix
- if (hit) window.open(`${D.githubBaseUrl}/commit/${hit.c.h}`, "_blank"); + if (hit) window.open(`${D.githubBaseUrl}/commit/${hit.c.h}`, "_blank", "noopener,noreferrer"); @@ - if (url) window.open(url, "_blank"); + if (url) window.open(url, "_blank", "noopener,noreferrer");Also applies to: 1179-1186
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/lib/timeline.ts` around lines 922 - 927, Replace plain window.open calls that use only "_blank" with a secure form that prevents the new page from accessing window.opener by passing noopener,noreferrer as the third argument; specifically update the window.open(...) in the canvas click handler (the one that uses findHit and opens `${D.githubBaseUrl}/commit/${hit.c.h}`) and the other similar window.open call later in the file so they call window.open(url, "_blank", "noopener,noreferrer").
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/App.svelte`:
- Around line 53-57: buildTimeline currently wires window/DOM listeners and
requestAnimationFrame loops but returns void, causing leaks on remount; change
buildTimeline to return a disposer function that cancels any pending rAFs and
removes all added event listeners (including window.resize and any DOM
listeners) and any animation callbacks, and ensure any resources from
createAuthorPopover/createCommitPopover that need cleanup are exposed/closed
inside that disposer or via their own dispose methods; then in onMount capture
the disposer (e.g., const teardown = buildTimeline(data, authorPopover,
createTimelineTooltip())) and return teardown from onMount so Svelte invokes it
on component destroy/remount.
In `@web/src/lib/actions.ts`:
- Around line 95-104: The onMouseDown handler starts a drag for any mouse
button; update onMouseDown to ignore non-primary buttons by returning early when
e.button !== 0 (i.e., only proceed for the primary/left button) before calling
stopInertia() and setting drag state (references: onMouseDown, stopInertia,
isDown, row.classList.add("dragging"), startX, scrollLeft, lastX, lastTime,
velocity).
In `@web/src/lib/components/Heatmap.svelte`:
- Around line 202-205: The anchor elements in Heatmap.svelte currently use
target="_blank" without rel and sometimes render href="#" when githubBaseUrl is
missing (see the anchor using cell.href and the earlier cell rendering logic),
which is insecure and poor UX; fix by only rendering an <a> when cell.href is a
non-empty, non-placeholder URL (or otherwise render a non-clickable element like
a <div>/<span>) and when keeping target="_blank" add rel="noopener noreferrer"
to the anchor, ensuring the logic that builds cell.href (the code referencing
githubBaseUrl and cell.href generation) does not fall back to "#" but yields
null/undefined so the conditional rendering can skip the link.
In `@web/src/lib/components/OverallCharts.svelte`:
- Around line 44-57: resetTimeline and resetPie call methods on timelineChart
and pieChart without ensuring the chart instances exist, which can throw if
invoked before onMount; guard each handler by checking the chart variable is
truthy before accessing its properties (e.g., return early if !timelineChart or
!pieChart), and apply the same existence checks to the other handlers referenced
around lines 211-216 so every chart access (getDatasetMeta, data,
getDataVisibility, toggleDataVisibility, update) is only executed when the
corresponding chart instance is initialized.
In `@web/src/lib/components/YearToggles.svelte`:
- Around line 27-33: The component currently conditionally renders the entire
toggle block based on years.length, which hides the "Current" button when years
is empty; change the rendering so the "Current" button (using the select
function and active variable) is always output and only the year-specific
buttons are wrapped in the conditional that checks years.length (i.e., keep the
<button ... onclick={() => select("current")}>Current</button> outside the {`#if`
years.length} block and move the years.map / {`#each` years as year} block inside
the conditional).
In `@web/vite.config.ts`:
- Line 32: The helper abs currently returns new URL(p, import.meta.url).pathname
which yields non-native Windows paths; update abs to use Node's fileURLToPath to
convert the URL to a proper filesystem path (import fileURLToPath from 'url' and
call fileURLToPath(new URL(p, import.meta.url))) so Vite resolve.alias paths
($components, $lib, $types) are correct on Windows; keep the function name abs
and only change the URL-to-path conversion.
---
Outside diff comments:
In `@web/src/lib/timeline.ts`:
- Around line 922-927: Replace plain window.open calls that use only "_blank"
with a secure form that prevents the new page from accessing window.opener by
passing noopener,noreferrer as the third argument; specifically update the
window.open(...) in the canvas click handler (the one that uses findHit and
opens `${D.githubBaseUrl}/commit/${hit.c.h}`) and the other similar window.open
call later in the file so they call window.open(url, "_blank",
"noopener,noreferrer").
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fca6e14e-93c0-40bc-8050-775de345ea2d
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockdist/repo-intelis excluded by!**/dist/**
📒 Files selected for processing (32)
.github/workflows/ci.yml.gitignore.prettierignore.prettierrcMakefileREADME.mdpackage.jsonweb/index.htmlweb/src/App.svelteweb/src/data.tsweb/src/lib/actions.tsweb/src/lib/components/AuthorPopover.svelteweb/src/lib/components/CommitPopover.svelteweb/src/lib/components/ContributorCard.svelteweb/src/lib/components/Header.svelteweb/src/lib/components/Heatmap.svelteweb/src/lib/components/LangBar.svelteweb/src/lib/components/OverallCharts.svelteweb/src/lib/components/PatternCard.svelteweb/src/lib/components/Table.svelteweb/src/lib/components/TechGrid.svelteweb/src/lib/components/TimelineTooltip.svelteweb/src/lib/components/YearToggles.svelteweb/src/lib/format.tsweb/src/lib/popover-store.svelte.tsweb/src/lib/popovers.tsweb/src/lib/theme.tsweb/src/lib/timeline.tsweb/src/styles/app.cssweb/src/types.tsweb/tsconfig.jsonweb/vite.config.ts
✅ Files skipped from review due to trivial changes (4)
- .prettierignore
- .prettierrc
- web/src/types.ts
- web/tsconfig.json
📜 Review details
🔇 Additional comments (25)
web/src/lib/popover-store.svelte.ts (2)
9-68: LGTM!
74-128: LGTM!web/src/lib/popovers.ts (2)
24-82: LGTM!
88-100: LGTM!.github/workflows/ci.yml (1)
24-31: LGTM!.gitignore (1)
9-10: LGTM!package.json (1)
1-13: LGTM!web/index.html (1)
9-11: LGTM!web/src/data.ts (1)
1-1: LGTM!Also applies to: 10-10
Makefile (1)
12-20: LGTM!Also applies to: 34-34
README.md (1)
50-50: LGTM!Also applies to: 209-245
web/src/lib/components/ContributorCard.svelte (1)
23-51: LGTM!Also applies to: 54-63, 65-105
web/src/lib/components/LangBar.svelte (1)
15-35: LGTM!Also applies to: 37-85
web/src/lib/components/Table.svelte (1)
12-145: LGTM!web/src/lib/components/AuthorPopover.svelte (1)
20-249: LGTM!web/src/lib/components/CommitPopover.svelte (1)
11-155: LGTM!web/src/lib/components/TimelineTooltip.svelte (1)
13-266: LGTM!web/src/lib/components/PatternCard.svelte (1)
13-95: LGTM!web/src/styles/app.css (1)
1-566: LGTM!web/src/lib/theme.ts (1)
21-28: LGTM!web/src/lib/format.ts (1)
2-2: LGTM!Also applies to: 31-36, 72-73
web/src/lib/actions.ts (1)
1-94: LGTM!Also applies to: 105-231
web/src/App.svelte (1)
3-52: LGTM!Also applies to: 60-160
web/src/lib/components/Header.svelte (1)
1-43: LGTM!web/src/lib/components/TechGrid.svelte (1)
1-111: LGTM!
…ws paths - dragScroll: ignore non-primary mouse buttons - Heatmap: drop href="#" fallback; gate href/target/rel so links carry rel=noopener noreferrer and non-linkable cells stay inert - timeline: add noopener,noreferrer to both window.open calls - vite.config: make abs() Windows-safe and percent-decoded without pulling in @types/node (keeps it out of svelte-check scope) Rebuilds dist/repo-intel.
What & why
Replaces the 1,740-line vanilla-JS
template.htmlmonolith with a componentized, type-safe frontend built with Bun + Vite + Svelte 5 + TypeScript. The monolith had become hard to navigate; the dashboard logic is now ~13 focused, typed modules.The end-user contract is unchanged.
vite-plugin-singlefileemits one self-containedweb/dist/index.html(all JS+CSS inlined) that still carries the/*__DATA_INJECTION__*/marker;build.pyembeds it intorepo-intel.pyexactly as before. Bun/Vite are build-time only — the shipped tool is still a zero-dependency Python executable installed via curl/Homebrew/Action.Highlights
web/src/lib/— dashboard engine split into one module per concern:heatmap,timeline(the big canvas one),charts,table,popovers,header,interactions, plusformat/theme/avatarhelpers, orchestrated bydashboard.ts.App.svelteholds the static structure;types.tsmirrorsbuild_data().dist/repo-intelis 418 kB.build.py/repo-intel.pyreadweb/dist/index.html;Makefilegainsweb-build/web-dev; the pre-commit hook rebuilds the bundle and watchesweb/(needs Bun);ci.yml/release.ymladdoven-sh/setup-bun@v2pinned to 1.3.10.template.html; updates README + .gitignore.Verification
Rendered end-to-end through the real Python pipeline with zero console errors and full visual parity at:
toss/es-toolkit— 1,552 commits, 234 contributors, ~2-year span, release tagsWatch on first CI run
The staleness check (
git diff --exit-code -- dist/repo-intel) assumes the Vite/esbuild bundle rebuilds byte-for-byte on Ubuntu CI vs the committer's machine. Same-machine rebuilds are bit-identical and Bun is pinned, but cross-OS reproducibility is unproven until this PR's CI runs. If it fails, weaken that step to "build succeeds + smoke test passes."DX note
Contributors now need Bun installed to commit any
web/-touching change (the pre-commit hook runsbun install && bun run build).Summary by CodeRabbit
New Features
Documentation
Chores