@@ -24,6 +24,13 @@ const ELEMENT_IDS = {
2424 UPDATE_TIME : "gce-update-time" ,
2525} ;
2626
27+ // Configuration constants
28+ const CONFIG = {
29+ CACHE_EXPIRATION : 7 * 24 * 60 * 60 * 1000 , // 7 days in milliseconds (was 24 hours)
30+ HOVER_DELAY : 100 , // milliseconds to wait before hiding panel
31+ STAT_PADDING : 3 , // number of digits to pad stats to
32+ } ;
33+
2734// Path and user detection helpers
2835const isPR = ( path ) => / ^ \/ [ ^ / ] + \/ [ ^ / ] + \/ p u l l \/ \d + / . test ( path ) ;
2936const isIssue = ( path ) => / ^ \/ [ ^ / ] + \/ [ ^ / ] + \/ i s s u e s \/ \d + / . test ( path ) ;
@@ -170,53 +177,31 @@ function issueOrPrLink(type, repoPath, contributor) {
170177 return `https://github.com/${ end } +user:${ repoPath } ` ;
171178}
172179
173- function createHoverPanelHTML ( { contributor, repoPath } ) {
180+ function createStatRow ( scope , label , contributor , repoPath ) {
174181 return `
175- <div id="${ ELEMENT_IDS . HOVER_PANEL } " class="position-absolute Box color-shadow-medium rounded-2 p-2" style="display: none; z-index: 100; top: 100%; left: -146px; min-width: 250px; margin-top: 4px;">
176- <!-- Repo stats -->
177- <div class="d-flex flex-items-center py-1">
178- <div class="gce-scope-label">
179- <span class="f6 color-fg-muted">In this repo:</span>
180- </div>
181- <div class="d-flex flex-items-center ml-auto">
182- <div class="d-inline-flex flex-items-center mr-2">
183- ${ ICONS . PR } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-repo-pr-count">...</span>
184- </div>
185- <div class="d-inline-flex flex-items-center">
186- ${ ICONS . ISSUE } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-repo-issue-count">...</span>
187- </div>
188- </div>
182+ <div class="d-flex flex-items-center py-1">
183+ <div class="gce-scope-label">
184+ <span class="f6 color-fg-muted">${ label } </span>
189185 </div>
190-
191- <!-- Org stats -->
192- <div class="d-flex flex-items-center py-1">
193- <div class="gce-scope-label">
194- <span class="f6 color-fg-muted">In this org:</span>
186+ <div class="d-flex flex-items-center ml-auto">
187+ <div class="d-inline-flex flex-items-center mr-2">
188+ ${ ICONS . PR } <a href="${ issueOrPrLink ( "pr" , scope === "repo" ? repoPath : scope === "org" ? repoPath : "__self" , contributor ) } " class="ml-1 Text-sc-17v1xeu-0 gce-stat-number Link--secondary" id="gce-${ scope } -pr-count">...</a>
195189 </div>
196- <div class="d-flex flex-items-center ml-auto">
197- <div class="d-inline-flex flex-items-center mr-2">
198- ${ ICONS . PR } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-org-pr-count">...</span>
199- </div>
200- <div class="d-inline-flex flex-items-center">
201- ${ ICONS . ISSUE } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-org-issue-count">...</span>
202- </div>
203- </div>
204- </div>
205-
206- <!-- Account stats -->
207- <div class="d-flex flex-items-center py-1">
208- <div class="gce-scope-label">
209- <span class="f6 color-fg-muted">In this account:</span>
210- </div>
211- <div class="d-flex flex-items-center ml-auto">
212- <div class="d-inline-flex flex-items-center mr-2">
213- ${ ICONS . PR } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-account-pr-count">...</span>
214- </div>
215- <div class="d-inline-flex flex-items-center">
216- ${ ICONS . ISSUE } <span class="ml-1 Text-sc-17v1xeu-0 gce-stat-number" id="gce-account-issue-count">...</span>
217- </div>
190+ <div class="d-inline-flex flex-items-center">
191+ ${ ICONS . ISSUE } <a href="${ issueOrPrLink ( "issue" , scope === "repo" ? repoPath : scope === "org" ? repoPath : "__self" , contributor ) } " class="ml-1 Text-sc-17v1xeu-0 gce-stat-number Link--secondary" id="gce-${ scope } -issue-count">...</a>
218192 </div>
219193 </div>
194+ </div>
195+ ` ;
196+ }
197+
198+ function createHoverPanelHTML ( { contributor, repoPath, org } ) {
199+ return `
200+ <div id="${ ELEMENT_IDS . HOVER_PANEL } " class="position-absolute Box color-shadow-medium rounded-2 p-2" style="display: none; z-index: 100; top: 100%; left: -146px; min-width: 250px; margin-top: 4px;">
201+ <!-- Stats rows -->
202+ ${ createStatRow ( "repo" , "In this repo:" , contributor , repoPath ) }
203+ ${ createStatRow ( "org" , "In this org:" , contributor , org ) }
204+ ${ createStatRow ( "account" , "In this account:" , contributor , "__self" ) }
220205
221206 <div class="border-top mt-1 mb-1"></div>
222207 <div class="d-flex flex-items-center">
@@ -255,10 +240,15 @@ function injectStyles() {
255240 flex-shrink: 0;
256241 }
257242 .gce-stat-number {
258- min-width: 24px ;
243+ min-width: 30px ;
259244 display: inline-block;
260245 text-align: right;
261246 font-variant-numeric: tabular-nums;
247+ text-decoration: none;
248+ }
249+ .gce-stat-number:hover {
250+ text-decoration: underline;
251+ color: var(--color-accent-fg);
262252 }
263253 #${ ELEMENT_IDS . SYNC_BUTTON } {
264254 padding: 2px 0;
@@ -296,7 +286,7 @@ function injectInitialUI({ contributor, repoPath, currentNum, org }) {
296286 ${ ICONS . ISSUE } <span class="ml-1 Text-sc-17v1xeu-0" style="font-size: 12px; line-height: 1.5;">${ "..." } </span>
297287 </a>
298288
299- ${ createHoverPanelHTML ( { contributor, repoPath } ) }
289+ ${ createHoverPanelHTML ( { contributor, repoPath, org } ) }
300290 </div>
301291 </div>` ,
302292 ) ;
@@ -332,7 +322,7 @@ function setupHoverBehavior() {
332322
333323 $statsContainer . addEventListener ( "mouseenter" , showPanel ) ;
334324 $statsContainer . addEventListener ( "mouseleave" , ( ) => {
335- setTimeout ( hidePanel , 100 ) ;
325+ setTimeout ( hidePanel , CONFIG . HOVER_DELAY ) ;
336326 } ) ;
337327
338328 $hoverPanel . addEventListener ( "mouseenter" , ( ) => {
@@ -366,6 +356,14 @@ function setupSyncButton({ contributor, repoPath, currentNum, org }) {
366356 } ) ;
367357}
368358
359+ // Check if cache is expired
360+ function isCacheExpired ( lastUpdate ) {
361+ if ( ! lastUpdate ) return true ;
362+
363+ const now = Date . now ( ) ;
364+ return now - lastUpdate > CONFIG . CACHE_EXPIRATION ;
365+ }
366+
369367// Fetch stats for all scopes (repo, org, account)
370368function fetchAllStats ( { contributor, repoPath, currentNum, org } ) {
371369 // Fetch repo stats
@@ -448,14 +446,17 @@ function fetchStats({ contributor, repoPath, currentNum, scope, user }) {
448446 getStorage ( contributor , repoPath ) . then ( ( storage ) => {
449447 const storageKey = `${ contributor } |${ user || repoPath } ` ;
450448 const storageRes = storage [ storageKey ] || { } ;
451-
452- if ( storageRes . prs !== undefined || storageRes . issues !== undefined ) {
449+
450+ // Check if we have valid data that's not expired
451+ if ( ( storageRes . prs !== undefined || storageRes . issues !== undefined ) &&
452+ ! isCacheExpired ( storageRes . lastUpdate ) ) {
453453 // Format the text for display
454454 const prText = formatText ( storageRes . prs , storageRes . firstPrNumber , currentNum , scope ) ;
455455 const issueText = formatText ( storageRes . issues , storageRes . firstIssueNumber , currentNum , scope ) ;
456456
457457 updateStatsDisplay ( { prText, issueText, scope, lastUpdate : storageRes . lastUpdate } ) ;
458458 } else {
459+ // If cache is expired or no data, fetch fresh data
459460 getSyncStorage ( { access_token : null } ) . then ( ( res ) => {
460461 Promise . all ( [
461462 contributorCount ( {
@@ -512,8 +513,8 @@ function padNumber(text) {
512513 // Only pad if it's a number
513514 const num = Number ( text ) ;
514515 if ( ! Number . isNaN ( num ) && text !== "..." ) {
515- // Right-align with space padding (only pad to 3 digits for compactness)
516- return text . toString ( ) . padStart ( 3 , ' ' ) ;
516+ // Right-align with space padding
517+ return text . toString ( ) . padStart ( CONFIG . STAT_PADDING , ' ' ) ;
517518 }
518519 return text ;
519520}
0 commit comments