Conversation
- Link the commits/contributors/branches counts to their GitHub pages (text-colour, underlined, no underline on hover). - Headline now matches GitHub's commit count: show authored + merge commits as "N commits (M merges)". totals.commits stays the merge-free authored count (= sum of contributors' commits, the share denominator); merges live in a sibling totals.merges composed in only at display time. - Add "Generated N ago" (header, right) and "Last commit N ago" (Contributions section), both relative with the full timestamp on hover. - Add GitHub stars/watchers/forks via REST (github.com only, 5s timeout, degrades to absent for non-GitHub or unreachable repos).
Tags on the same commit (shared oid) draw on one dot, so only the topmost was discoverable. The tag tip now lists every tag at that commit as a two-column table (names aligned, hairline row separators), with the shared date and oid below.
The Sat/Sun grid line sits inside the weekend tint band, which amplified it into the chart's boldest rule. Lower the uniform splitLine opacity so it reads as a normal subdivision again. Tints unchanged.
The weekly-commits and commit-share charts aggregate contributors by email, so one person committing under several addresses shows up as several rows sharing a display name. ECharts keys legend selection by series name, collapsing those into one toggle. Name the series/slices by email instead, hide ECharts' own legend, and render HTML legends that toggle selection and carry the same author popover the timeline lanes use. Add matching name links + popovers to the contributor and pattern cards, with aria-pressed on the legend toggles and non-navigating labels on local-only repos.
Convert the commit-share donut to a larger solid pie with outside name + share labels. minShowLabelAngle drops the label (and its leader line) for sectors under ~4% of the circle; being angle-based, labels reappear as the legend hides bigger slices and the survivors widen. Hovering a legend row now highlights its slice, scaling it out a touch. Also skip bot accounts (logins ending in [bot]) in the GraphQL profile fetch — they're Bot nodes, not User nodes, so user(login:) only ever returns NOT_FOUND. Benign NOT_FOUND errors are no longer warned about.
Replace the grouped added/deleted bars with horizontal diverging bars (deleted left of zero, added right), sorted by total churn and filtered of [bot] accounts so the human contributors keep the scale. The y-axis labels carry the identity dot, name, and author popover used elsewhere, and click through to the contributor's commits. The legend moves into the title as colour squares, and the value labels drop their stroke.
The languages treemap header carried each contributor's commit share, but the names truncate at narrow widths and re-anchoring the hover popover on every mouseover (one per zrender sub-element of the header strip) made it jerk. Drop the header percentage, key the popover on the contributor idx so it anchors once per section, and surface the share on the popover's commit line instead — shared across the table, timeline, churn axis, and treemap.
Addresses a code-review pass over the branch: - repo-intel.py: the merge tally is whole-history (collected before filtering, and merges aren't in commits_meta to recount), so adding it to a date/subset-filtered authored total inflated the "N commits" headline. Drop it under any --since/--until/--commits filter so the headline reads as just the commits in range. - OverallCharts.svelte: escape contributor names in the four tooltip formatters whose output is rendered as HTML (a name with markup would otherwise inject). Rich-text/canvas labels are left as-is. - ContributorCard/PatternCard.svelte: gate target/rel on a real href so local-only repos (url "#") render a clean non-navigating label rather than dangling link attributes. - Header.svelte: collapse the three repeated link-or-plaintext subtitle branches into a single `stat` snippet. Rebuild dist/repo-intel.
OverallCharts.svelte (1212 lines) became a 28-line grid orchestrator; each chart now lives in its own component following the ContributorCard/PatternCard convention: - WeeklyCommitsCard, CommitShareCard, ChurnCard, CommitStyleCard, LanguagesCard, LargestFilesCard (each owns its option + handlers) - ChartLegend: shared HTML legend + Reset for the two legend-driven charts - chart-helpers.ts: shared name/email/legend builders + tileInnerBorder Languages and files cards self-guard their own visibility. Promoted the cross-component layout primitives (.chart-card/.ec/.ec-tree/.span-2) into app.css; per-card style overrides stay collocated.
- Classify .po/.pot as "Gettext Catalog" (gold #9e6a03, GitHub's attention fallback for colorless Linguist languages) instead of letting translation catalogs vanish into "Other". Pinned via gen_techdata.py's EXT_OVERRIDE + SYNTHETIC_COLORS (merge reordered before the override guard) so a `make techdata` regen reproduces it rather than dropping it. - Fix langSearchUrl: emit a path-glob query for buckets GitHub can't resolve by language (Gettext Catalog -> path:*.po OR path:*.pot), suppress the unresolvable Tools link, and quote multi-word language names so `language:"Common Lisp"` parses. - Link "Languages by contributor" treemap cells. GitHub code search has no author filter, so the two dimensions split across the cell: the header strip opens the author's commit log, each language tile opens a repo-wide code search for that language. - Raise the language limit 6 -> 10 before collapsing into "Other".
WeeklyCommitsCard and CommitShareCard each carried an identical copy of the hidden-legend plumbing (sel state, chart ref, onReady/toggle/reset, plus highlight on the pie). Pull it into createLegendSelection() in a new chart-legend.svelte.ts module (the popover-store.svelte.ts -module convention), exposing selected via a getter so callers stay reactive.
ChurnCard and CommitStyleCard both open-coded the non-bot contributor
filter (map to {c, origIdx}, drop logins ending in [bot]). Pull it into
humanContribRows() in chart-helpers.ts so the bot-detection rule lives in
one place and the two charts can't drift on what counts as a bot.
The generated dashboard is a single HTML file opened over file:// from /tmp, so a sibling or remote favicon never loads. vite-plugin-singlefile only inlines JS/CSS, and public/ files are copied verbatim — so the icon shipped as an unreachable ./favicon.png link. Move the favicon out of public/ into src/assets so Vite tracks it as a build asset, and add an inlineFavicon() plugin that reads its bytes from the bundle and rewrites the <link> href to a data: URI (no node:fs, like the existing abs() helper). One self-contained file, no emitted sibling.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR refactors the dashboard frontend into modular chart components while enhancing backend data collection. The backend now tracks merge commits, fetches GitHub social statistics, and adds Gettext Catalog language support. The frontend extracts six chart cards from a monolithic OverallCharts component, implements multi-tag timeline tooltips, and threads author popovers throughout the UI to show commit share and contributor details on hover. ChangesBackend: Statistics and Language Support
Frontend: Data, State, and Timeline
Frontend: Chart Architecture
Frontend: Integration and Display
Frontend: Styling and Build
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 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)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
web/vite.config.ts (1)
50-51: ⚡ Quick winReplace
btoawith Buffer and make favicon href replacement deterministic
- Line 50:
btoa(binary)relies on legacy Node globals and requires an extra bytes→string loop; preferBuffer.from(bytes).toString("base64").- Line 51:
new RegExp(\\.?/${fav.fileName}`)interpolates an unescaped filename into a regex; use exact string replacements for./${fav.fileName}and/${fav.fileName}` (or escape regex metacharacters).🤖 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/vite.config.ts` around lines 50 - 51, Replace the legacy btoa-based base64 conversion and the unsafe regex replacement: instead of using btoa(binary) build the data URI with Buffer.from(binary).toString("base64") (use Buffer.from to convert the bytes), and replace the html.source.replace(new RegExp(...)) call with deterministic exact string replacements for both `./${fav.fileName}` and `/${fav.fileName}` (or escape fav.fileName) so html.source.replace targets the literal paths; update the code around dataUri, btoa, html.source.replace, and fav.fileName accordingly.web/src/lib/components/LargestFilesCard.svelte (1)
93-99: 💤 Low valuePotential
Infinity/-InfinitywhenleafTotalsis empty.If
payload.itemscontains only root-level files withoutfullPathset, or only the "N more files" remainder,leafTotalscould be empty.Math.min(...[])returnsInfinityandMath.max(...[])returns-Infinity, which would makelo > hiand causegrey()to always returnt = 1.In practice this is unlikely given the data shape, but a guard would be safer:
Suggested defensive guard
const leafTotals: number[] = []; for (const n of nodes) { if (n.children) for (const c of n.children) leafTotals.push(c.total); else if (n.fullPath) leafTotals.push(n.total); } + if (leafTotals.length === 0) leafTotals.push(1); // fallback to avoid Infinity const lo = Math.log(Math.max(Math.min(...leafTotals), 1)); const hi = Math.log(Math.max(...leafTotals, 1));🤖 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/components/LargestFilesCard.svelte` around lines 93 - 99, The code computes lo and hi from leafTotals but doesn't handle an empty leafTotals array, causing Math.min(...leafTotals) to be Infinity and lo>hi; add a guard: if leafTotals.length === 0 set safe defaults (e.g. lo = 0 and hi = 0 or hi = 1) before computing logs, otherwise compute lo and hi as now; reference leafTotals, lo, hi and ensure the grey() caller gets a sensible range (or clamp t) so it won’t always return t = 1 when leafTotals is empty.
🤖 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/lib/components/ChartLegend.svelte`:
- Line 72: The Reset button in ChartLegend.svelte remains focusable/accessible
when hidden because it only uses opacity; update the element so when hasHidden
is false it is removed from the accessibility tree and not focusable: modify the
<button class="chart-reset-btn" class:visible={hasHidden}
onclick={onReset}>Reset</button> to conditionally render or toggle attributes
(e.g., render only when hasHidden, or add aria-hidden="true" and tabindex="-1"
and pointer-events: none/visibility:hidden when not visible) so the button is
neither tabbable nor announced by screen readers when hidden; apply the same fix
to the other hidden controls referenced around lines 133-155.
In `@web/src/lib/popover-store.svelte.ts`:
- Around line 127-136: The identity check in setTagTip currently only compares
tags[0]?.oid and length, which fails for tag groups keyed by date (no oid) and
allows stale tooltips; update the condition in setTagTip to compare a stable key
for the group: if tags[0]?.oid exists use oid, otherwise compare the date (e.g.,
tags[0]?.date or equivalent timestamp) along with the length, so
timelineTipState.tags is updated when the date-keyed group changes; adjust the
branch that sets timelineTipState.kind and timelineTipState.tags accordingly.
---
Nitpick comments:
In `@web/src/lib/components/LargestFilesCard.svelte`:
- Around line 93-99: The code computes lo and hi from leafTotals but doesn't
handle an empty leafTotals array, causing Math.min(...leafTotals) to be Infinity
and lo>hi; add a guard: if leafTotals.length === 0 set safe defaults (e.g. lo =
0 and hi = 0 or hi = 1) before computing logs, otherwise compute lo and hi as
now; reference leafTotals, lo, hi and ensure the grey() caller gets a sensible
range (or clamp t) so it won’t always return t = 1 when leafTotals is empty.
In `@web/vite.config.ts`:
- Around line 50-51: Replace the legacy btoa-based base64 conversion and the
unsafe regex replacement: instead of using btoa(binary) build the data URI with
Buffer.from(binary).toString("base64") (use Buffer.from to convert the bytes),
and replace the html.source.replace(new RegExp(...)) call with deterministic
exact string replacements for both `./${fav.fileName}` and `/${fav.fileName}`
(or escape fav.fileName) so html.source.replace targets the literal paths;
update the code around dataUri, btoa, html.source.replace, and fav.fileName
accordingly.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0ad72c04-fc67-4d7f-97f7-9c663c9bda4e
⛔ Files ignored due to path filters (2)
dist/repo-intelis excluded by!**/dist/**web/src/assets/favicon.pngis excluded by!**/*.png
📒 Files selected for processing (30)
MakefileREADME.mdgen_techdata.pyrepo-intel.pytechdata.jsonweb/index.htmlweb/src/App.svelteweb/src/lib/chart-helpers.tsweb/src/lib/chart-legend.svelte.tsweb/src/lib/components/AuthorPopover.svelteweb/src/lib/components/ChartLegend.svelteweb/src/lib/components/ChurnCard.svelteweb/src/lib/components/CommitShareCard.svelteweb/src/lib/components/CommitStyleCard.svelteweb/src/lib/components/ContributorCard.svelteweb/src/lib/components/Header.svelteweb/src/lib/components/LanguagesCard.svelteweb/src/lib/components/LargestFilesCard.svelteweb/src/lib/components/OverallCharts.svelteweb/src/lib/components/PatternCard.svelteweb/src/lib/components/TechGrid.svelteweb/src/lib/components/TimelineTooltip.svelteweb/src/lib/components/WeeklyCommitsCard.svelteweb/src/lib/format.tsweb/src/lib/popover-store.svelte.tsweb/src/lib/popovers.tsweb/src/lib/timeline.tsweb/src/styles/app.cssweb/src/types.tsweb/vite.config.ts
- ChartLegend: hide Reset from tab order/AT and make it click-inert when no series is hidden (was opacity-only, still focusable/clickable). - setTagTip: key group identity on oid || date so lightweight tags (no oid) don't collide and stick a stale tooltip across date groups.
Summary
A dashboard-focused pass over the
web/Svelte app: the monolithicOverallChartsis broken into per-chart cards, charts are recast for clarity, and shared chart/legend logic is extracted into reusable helpers. Also adds a header refresh, language-treemap GitHub links, a self-contained favicon, and a tag-sync caveat in the README.Charts & components
OverallChartsinto per-chart cards —ChurnCard,CommitShareCard,CommitStyleCard,LanguagesCard,LargestFilesCard,WeeklyCommitsCard, dropsOverallChartsfrom ~770 lines to a thin shell.ChartLegendcomponent and achart-legend.svelte.tsselection-state factory.humanContribRows.Header & UI polish
Data & build
.poas Gettext Catalog and linked the language treemap to GitHub.inlineFavicon()invite.config.ts) so single-file dashboards render it overfile://.vXtag) and added agit gcmake target.Test plan
make buildregeneratesdist/repo-intel(committed).bun run checkinweb/passes.Summary by CodeRabbit
New Features
Documentation
Chores