@@ -35,8 +35,19 @@ class LilkaRepository {
3535 // Handle authors page
3636 if ( type === 'authors' ) {
3737 this . currentType = type ;
38+ const authorParam = params . get ( 'author' ) ;
3839 await this . showAuthors ( ) ;
3940 this . updateActiveTab ( 'authors' ) ;
41+ if ( authorParam ) {
42+ const sectionId = `author-${ this . authorSlug ( authorParam ) } ` ;
43+ const section = document . getElementById ( sectionId ) ;
44+ if ( section ) {
45+ section . classList . remove ( 'collapsed' ) ;
46+ section . scrollIntoView ( { behavior : 'smooth' , block : 'start' } ) ;
47+ section . classList . add ( 'author-highlight' ) ;
48+ setTimeout ( ( ) => section . classList . remove ( 'author-highlight' ) , 2000 ) ;
49+ }
50+ }
4051 return ;
4152 }
4253
@@ -60,9 +71,12 @@ class LilkaRepository {
6071 document . querySelector ( `[data-type="${ type } "]` ) . classList . add ( 'active' ) ;
6172
6273 // Show/hide appropriate content
63- document . getElementById ( 'content' ) . style . display = ( type === 'docs' || type === 'authors' ) ? 'none' : 'block' ;
64- document . getElementById ( 'docs' ) . style . display = type === 'docs' ? 'block' : 'none' ;
65- document . getElementById ( 'authors' ) . style . display = type === 'authors' ? 'block' : 'none' ;
74+ document . getElementById ( 'content' ) . style . display =
75+ ( type === 'docs' || type === 'authors' ) ? 'none' : 'block' ;
76+ document . getElementById ( 'docs' ) . style . display =
77+ type === 'docs' ? 'block' : 'none' ;
78+ document . getElementById ( 'authors' ) . style . display =
79+ type === 'authors' ? 'block' : 'none' ;
6680 }
6781
6882 async openDirectItem ( type , itemName ) {
@@ -269,15 +283,17 @@ class LilkaRepository {
269283 } ) ;
270284
271285 // Collapsible author sections
272- authorsContainer . querySelectorAll ( '.author-section-header' ) . forEach ( header => {
273- header . addEventListener ( 'click' , ( ) => {
274- const section = header . closest ( '.author-section' ) ;
275- section . classList . toggle ( 'collapsed' ) ;
276- } ) ;
277- } ) ;
286+ authorsContainer . querySelectorAll ( '.author-section-header' )
287+ . forEach ( header => {
288+ header . addEventListener ( 'click' , ( ) => {
289+ const section = header . closest ( '.author-section' ) ;
290+ section . classList . toggle ( 'collapsed' ) ;
291+ } ) ;
292+ } ) ;
278293 } catch ( error ) {
279294 authorsContainer . innerHTML =
280- `<p style="color: var(--error);">Failed to load authors: ${ error . message } </p>` ;
295+ `<p style="color: var(--error);">Failed to load authors: ${
296+ error . message } </p>`;
281297 }
282298 }
283299
@@ -290,28 +306,38 @@ class LilkaRepository {
290306 const apps = items . filter ( i => i . type === 'apps' ) ;
291307 const mods = items . filter ( i => i . type === 'mods' ) ;
292308 const badge = [ ] ;
293- if ( apps . length ) badge . push ( `${ apps . length } app${ apps . length > 1 ? 's' : '' } ` ) ;
294- if ( mods . length ) badge . push ( `${ mods . length } mod${ mods . length > 1 ? 's' : '' } ` ) ;
309+ if ( apps . length )
310+ badge . push ( `${ apps . length } app${ apps . length > 1 ? 's' : '' } ` ) ;
311+ if ( mods . length )
312+ badge . push ( `${ mods . length } mod${ mods . length > 1 ? 's' : '' } ` ) ;
295313
296- html += `<div class="author-section">` ;
314+ const sectionId = `author-${ this . authorSlug ( author ) } ` ;
315+ html += `<div class="author-section" id="${ sectionId } ">` ;
297316 html += `<div class="author-section-header">` ;
298- html += `<span class="author-section-name">${ this . escapeHtml ( author ) } </span>` ;
317+ html +=
318+ `<span class="author-section-name">${ this . escapeHtml ( author ) } </span>` ;
299319 html += `<span class="author-section-badge">${ badge . join ( ', ' ) } </span>` ;
300320 html += `<span class="author-section-toggle">▼</span>` ;
301321 html += `</div>` ;
302322 html += `<div class="author-section-body">` ;
303323 html += `<div class="author-items-grid">` ;
304324
305325 for ( const item of items ) {
306- const iconPath = item . icon ? `${ item . type } /${ item . path } /static/${ item . icon } ` : '' ;
326+ const iconPath =
327+ item . icon ? `${ item . type } /${ item . path } /static/${ item . icon } ` : '' ;
307328 const typeLabel = item . type === 'apps' ? 'App' : 'Mod' ;
308- html += `<div class="author-item-card" data-item-type="${ item . type } " data-item-path="${ this . escapeHtml ( item . path ) } ">` ;
329+ html += `<div class="author-item-card" data-item-type="${
330+ item . type } " data-item-path="${ this . escapeHtml ( item . path ) } ">`;
309331 if ( item . icon ) {
310- html += `<img src="${ iconPath } " alt="${ this . escapeHtml ( item . name ) } " class="icon" onerror="this.style.display='none'">` ;
332+ html += `<img src="${ iconPath } " alt="${
333+ this . escapeHtml (
334+ item . name ) } " class="icon" onerror="this.style.display='none'">`;
311335 }
312336 html += `<h3>${ this . escapeHtml ( item . name ) } </h3>` ;
313- html += `<span class="author-item-type type-${ item . type } ">${ typeLabel } </span>` ;
314- html += `<div class="short-desc">${ this . escapeHtml ( item . short_description ) } </div>` ;
337+ html += `<span class="author-item-type type-${ item . type } ">${
338+ typeLabel } </span>`;
339+ html += `<div class="short-desc">${
340+ this . escapeHtml ( item . short_description ) } </div>`;
315341 html += `</div>` ;
316342 }
317343
@@ -423,6 +449,7 @@ class LilkaRepository {
423449 const iconPath =
424450 `${ this . currentType } /${ manifestName } /static/${ manifest . icon } ` ;
425451
452+ const authorId = this . authorSlug ( manifest . author ) ;
426453 card . innerHTML = `
427454 ${
428455 manifest . icon ?
@@ -431,11 +458,25 @@ class LilkaRepository {
431458 . name } " class="icon" onerror="this.style.display='none'">` :
432459 '' }
433460 <h3>${ this . escapeHtml ( manifest . name ) } </h3>
434- <div class="author">${ this . escapeHtml ( manifest . author ) } </div>
461+ <div class="author"><a href="?type=authors&author=${
462+ encodeURIComponent (
463+ manifest . author ) } " class="author-link" data-author="${
464+ this . escapeHtml (
465+ manifest . author ) } ">${ this . escapeHtml ( manifest . author ) } </a></div>
435466 <div class="short-desc">${
436467 this . escapeHtml ( manifest . short_description ) } </div>
437468 ` ;
438469
470+ // Author link click — navigate to authors page
471+ const authorLink = card . querySelector ( '.author-link' ) ;
472+ if ( authorLink ) {
473+ authorLink . addEventListener ( 'click' , ( e ) => {
474+ e . preventDefault ( ) ;
475+ e . stopPropagation ( ) ;
476+ this . navigateToAuthor ( manifest . author ) ;
477+ } ) ;
478+ }
479+
439480 card . addEventListener ( 'click' , ( ) => {
440481 // Track card clicks to view details
441482 if ( window . umami ) {
@@ -564,8 +605,11 @@ class LilkaRepository {
564605 filesSection += `
565606 <div class="modal-section">
566607 <h3>📦 Package ZIP</h3>
567- <p><strong>File:</strong> ${ this . escapeHtml ( manifest . package ) } </p>
568- <a href="${ packageDownloadPath } " download class="download-btn" data-umami-event="download-package-zip" data-umami-event-item="${ this . escapeHtml ( manifest . name ) } ">⬇️ Download ZIP</a>
608+ <p><strong>File:</strong> ${
609+ this . escapeHtml ( manifest . package ) } </p>
610+ <a href="${
611+ packageDownloadPath } " download class="download-btn" data-umami-event="download-package-zip" data-umami-event-item="${
612+ this . escapeHtml ( manifest . name ) } ">⬇️ Download ZIP</a>
569613 </div>
570614 ` ;
571615 }
@@ -629,7 +673,11 @@ class LilkaRepository {
629673 modalBody . innerHTML = `
630674 <div class="modal-header">
631675 <h2>${ this . escapeHtml ( manifest . name ) } </h2>
632- <div class="author">${ this . escapeHtml ( manifest . author ) } </div>
676+ <div class="author"><a href="?type=authors&author=${
677+ encodeURIComponent (
678+ manifest . author ) } " class="author-link" data-author="${
679+ this . escapeHtml (
680+ manifest . author ) } ">${ this . escapeHtml ( manifest . author ) } </a></div>
633681 </div>
634682 ${
635683 manifest . icon ?
@@ -680,6 +728,16 @@ class LilkaRepository {
680728 this . openLightbox ( index ) ;
681729 } ) ;
682730 } ) ;
731+
732+ // Add click handler for author link in modal
733+ const modalAuthorLink = modalBody . querySelector ( '.author-link' ) ;
734+ if ( modalAuthorLink ) {
735+ modalAuthorLink . addEventListener ( 'click' , ( e ) => {
736+ e . preventDefault ( ) ;
737+ e . stopPropagation ( ) ;
738+ this . navigateToAuthor ( modalAuthorLink . dataset . author ) ;
739+ } ) ;
740+ }
683741 } , 100 ) ;
684742 }
685743
@@ -839,6 +897,36 @@ class LilkaRepository {
839897 div . textContent = text ;
840898 return div . innerHTML ;
841899 }
900+
901+ authorSlug ( author ) {
902+ return ( author || '' ) . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) . toLowerCase ( ) ;
903+ }
904+
905+ async navigateToAuthor ( author ) {
906+ // Close modal if open
907+ document . getElementById ( 'modal' ) . style . display = 'none' ;
908+
909+ // Load authors page
910+ await this . showAuthors ( ) ;
911+
912+ // Scroll to the author section & highlight it
913+ const sectionId = `author-${ this . authorSlug ( author ) } ` ;
914+ const section = document . getElementById ( sectionId ) ;
915+ if ( section ) {
916+ // Make sure it's not collapsed
917+ section . classList . remove ( 'collapsed' ) ;
918+ section . scrollIntoView ( { behavior : 'smooth' , block : 'start' } ) ;
919+ section . classList . add ( 'author-highlight' ) ;
920+ setTimeout ( ( ) => section . classList . remove ( 'author-highlight' ) , 2000 ) ;
921+ }
922+
923+ // Update URL
924+ const params = new URLSearchParams ( ) ;
925+ params . set ( 'type' , 'authors' ) ;
926+ params . set ( 'author' , author ) ;
927+ window . history . pushState (
928+ { type : 'authors' , author} , '' , `?${ params . toString ( ) } ` ) ;
929+ }
842930}
843931
844932// Initialize the app when DOM is ready
0 commit comments