@@ -95,6 +95,8 @@ const translations = {
9595
9696 // Changelog Page
9797 ch_pg_title : "Evolution of Skrypter" , ch_pg_desc : "See how we transformed from a simple tool into an enterprise engine." ,
98+ ch_loading : "Loading changelog from GitHub..." ,
99+ ch_error : "Could not load changelog. View it directly on GitHub." ,
98100 ch_v211_date : "May 12, 2026" ,
99101 ch_v211_title : "Version 2.1.1 - Maintenance & User Requests" ,
100102 ch_v211_1 : "Added: User-Defined SQL Timeout setting in Configuration panel." ,
@@ -197,7 +199,8 @@ const translations = {
197199
198200
199201 // Download
200- dl_title : "Download SQLSkrypter" , dl_ver : "Version: 2.1.1" , dl_date : "Release Date: May 12, 2026" , dl_btn : "Download Installer (.exe)" , dl_sys : "System Requirements: Windows 10/11 x64. .NET 8.0 Desktop Runtime. Minimum 4GB RAM." ,
202+ dl_title : "Download SQLSkrypter" , dl_btn : "Download Installer (.exe)" , dl_loading : "Loading latest version..." ,
203+ dl_sys : "System Requirements: Windows 10/11 x64. .NET 8.0 Desktop Runtime. Minimum 4GB RAM." ,
201204
202205 // Footer
203206 ft_manual : "User Manual" , ft_faq : "FAQ" , ft_licd : "Licensing" , ft_change : "Changelog" ,
@@ -299,6 +302,8 @@ const translations = {
299302
300303 // Changelog Page
301304 ch_pg_title : "Ewolucja Oprogramowania" , ch_pg_desc : "Sprawdź, jak ewoluowaliśmy od małego narzędzia do potężnego silnika bazodanowego." ,
305+ ch_loading : "Ładowanie changelogu z GitHub..." ,
306+ ch_error : "Nie można załadować changelogu. Sprawdź go bezpośrednio na GitHub." ,
302307 ch_v211_date : "12 maja 2026" ,
303308 ch_v211_title : "Wersja 2.1.1 - Konserwacja i Prośby Użytkowników" ,
304309 ch_v211_1 : "Dodano: Możliwość ręcznego ustawienia SQL Timeout w panelu Konfiguracji." ,
@@ -388,14 +393,185 @@ const translations = {
388393
389394
390395 // Download
391- dl_title : "Pobierz SQLSkrypter" , dl_ver : "Wersja: 2.1.1" , dl_date : "Data Wydania: 12 maja 2026" , dl_btn : "Instalator (.exe)" , dl_sys : "Wymaganania techniczne: Windows 10/11 x64, .NET 8.0 Desktop Runtime, Min. 4GB RAM CPU wielordzeniowy (dla operacji równolełych)." ,
396+ dl_title : "Pobierz SQLSkrypter" , dl_btn : "Instalator (.exe)" , dl_loading : "Pobieranie najnowszej wersji..." ,
397+ dl_sys : "Wymaganania techniczne: Windows 10/11 x64, .NET 8.0 Desktop Runtime, Min. 4GB RAM CPU wielordzeniowy (dla operacji równolełych)." ,
392398
393399 // Footer
394400 ft_manual : "Dokumentacja" , ft_faq : "FAQ" , ft_licd : "Licencje" , ft_change : "Changelog" ,
395401 ft_priv : "Prywatność" , ft_terms : "Regulamin" , ft_contact : "Kontakt"
396402 }
397403} ;
398404
405+ // --- GitHub Changelog (dynamic from all releases) ---
406+ function fetchChangelog ( ) {
407+ const timeline = document . getElementById ( 'gh-changelog-timeline' ) ;
408+ const loading = document . getElementById ( 'gh-changelog-loading' ) ;
409+ const errorBox = document . getElementById ( 'gh-changelog-error' ) ;
410+ if ( ! timeline || ! loading ) return ;
411+
412+ const lang = localStorage . getItem ( 'sqlskrypter_lang' ) || 'en' ;
413+
414+ fetch ( 'https://api.github.com/repos/rotgamedev/SQLSkrypter/releases?per_page=20' , {
415+ headers : { 'Accept' : 'application/vnd.github+json' }
416+ } )
417+ . then ( res => {
418+ if ( ! res . ok ) throw new Error ( 'API error' ) ;
419+ return res . json ( ) ;
420+ } )
421+ . then ( releases => {
422+ loading . remove ( ) ;
423+
424+ if ( ! releases . length ) {
425+ errorBox . style . display = 'block' ;
426+ return ;
427+ }
428+
429+ const ICON = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"></polyline><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>` ;
430+
431+ // Colour-coded badge per section header keyword
432+ const BADGE_COLORS = {
433+ 'added' : '#4ade80' ,
434+ 'new' : '#4ade80' ,
435+ 'changed' : '#facc15' ,
436+ 'improved' :'#facc15' ,
437+ 'fixed' : '#f87171' ,
438+ 'removed' : '#f87171' ,
439+ } ;
440+
441+ function sectionColor ( header ) {
442+ const lower = header . toLowerCase ( ) ;
443+ for ( const [ key , color ] of Object . entries ( BADGE_COLORS ) ) {
444+ if ( lower . includes ( key ) ) return color ;
445+ }
446+ return 'var(--accent-blue)' ;
447+ }
448+
449+ // Lightweight Markdown → timeline HTML converter
450+ function parseBody ( body ) {
451+ if ( ! body || ! body . trim ( ) ) return '' ;
452+ let html = '' ;
453+ let inList = false ;
454+ const lines = body . split ( / \r ? \n / ) ;
455+
456+ for ( const raw of lines ) {
457+ const line = raw . trim ( ) ;
458+ if ( ! line ) {
459+ if ( inList ) { html += '</ul>' ; inList = false ; }
460+ continue ;
461+ }
462+
463+ // ### Section header
464+ if ( line . startsWith ( '###' ) ) {
465+ if ( inList ) { html += '</ul>' ; inList = false ; }
466+ const title = line . replace ( / ^ # + \s * / , '' ) ;
467+ const color = sectionColor ( title ) ;
468+ html += `<p style="font-weight:700;margin:1.2rem 0 0.4rem;color:${ color } ;font-size:0.85rem;text-transform:uppercase;letter-spacing:0.05em;">${ title } </p>` ;
469+ continue ;
470+ }
471+
472+ // ## or # header (release name duplicate — skip)
473+ if ( line . startsWith ( '#' ) ) continue ;
474+
475+ // Bullet point
476+ if ( line . startsWith ( '- ' ) || line . startsWith ( '* ' ) ) {
477+ if ( ! inList ) { html += '<ul class="highlight-list">' ; inList = true ; }
478+ const text = line . slice ( 2 ) . replace ( / ` ( [ ^ ` ] + ) ` / g, '<code style="background:rgba(152,221,255,0.1);padding:1px 5px;border-radius:4px;font-size:0.9em;">$1</code>' ) ;
479+ html += `<li>${ ICON } <span>${ text } </span></li>` ;
480+ continue ;
481+ }
482+
483+ // Plain paragraph text
484+ if ( inList ) { html += '</ul>' ; inList = false ; }
485+ const text = line . replace ( / ` ( [ ^ ` ] + ) ` / g, '<code style="background:rgba(152,221,255,0.1);padding:1px 5px;border-radius:4px;font-size:0.9em;">$1</code>' ) ;
486+ html += `<p style="color:var(--text-secondary);margin:0.3rem 0;">${ text } </p>` ;
487+ }
488+
489+ if ( inList ) html += '</ul>' ;
490+ return html ;
491+ }
492+
493+ releases . forEach ( ( release , index ) => {
494+ const tag = ( release . tag_name || '' ) . replace ( / ^ v / i, '' ) ;
495+ const name = release . name || release . tag_name || tag ;
496+ const rawDate = release . published_at ;
497+ const dateObj = rawDate ? new Date ( rawDate ) : null ;
498+ const dateStr = dateObj
499+ ? dateObj . toLocaleDateString ( lang === 'pl' ? 'pl-PL' : 'en-US' , { day : 'numeric' , month : 'long' , year : 'numeric' } )
500+ : '' ;
501+
502+ const isLatest = index === 0 ;
503+ const latestBadge = isLatest
504+ ? `<span style="display:inline-block;margin-left:0.75rem;padding:2px 10px;font-size:0.7rem;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;background:var(--accent-blue);color:#000;border-radius:100px;">latest</span>`
505+ : '' ;
506+
507+ const bodyHtml = parseBody ( release . body ) ;
508+
509+ const item = document . createElement ( 'div' ) ;
510+ item . className = 'timeline-item' ;
511+ item . innerHTML = `
512+ <span class="date">${ dateStr } </span>
513+ <h3>${ name } ${ latestBadge } </h3>
514+ ${ bodyHtml || `<p style="color:var(--text-muted);font-style:italic;">No release notes.</p>` }
515+ <a href="${ release . html_url } " target="_blank" rel="noopener"
516+ style="display:inline-flex;align-items:center;gap:6px;margin-top:1rem;font-size:0.8rem;color:var(--accent-blue);opacity:0.7;text-decoration:none;"
517+ onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.7">
518+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
519+ View on GitHub
520+ </a>
521+ ` ;
522+ timeline . appendChild ( item ) ;
523+ } ) ;
524+ } )
525+ . catch ( ( ) => {
526+ if ( loading ) loading . remove ( ) ;
527+ if ( errorBox ) errorBox . style . display = 'block' ;
528+ } ) ;
529+ }
530+
531+ // --- GitHub Latest Release Badge ---
532+ function fetchLatestRelease ( ) {
533+ const badge = document . getElementById ( 'gh-release-badge' ) ;
534+ if ( ! badge ) return ; // Only run on pages that have the badge
535+
536+ const lang = localStorage . getItem ( 'sqlskrypter_lang' ) || 'en' ;
537+
538+ fetch ( 'https://api.github.com/repos/rotgamedev/SQLSkrypter/releases/latest' , {
539+ headers : { 'Accept' : 'application/vnd.github+json' }
540+ } )
541+ . then ( res => {
542+ if ( ! res . ok ) throw new Error ( 'API error' ) ;
543+ return res . json ( ) ;
544+ } )
545+ . then ( data => {
546+ const tag = data . tag_name || '' ;
547+ const version = tag . replace ( / ^ v / i, '' ) ;
548+ const rawDate = data . published_at ;
549+ const dateObj = rawDate ? new Date ( rawDate ) : null ;
550+
551+ let dateStr = '' ;
552+ if ( dateObj ) {
553+ if ( lang === 'pl' ) {
554+ dateStr = dateObj . toLocaleDateString ( 'pl-PL' , { day : 'numeric' , month : 'long' , year : 'numeric' } ) ;
555+ } else {
556+ dateStr = dateObj . toLocaleDateString ( 'en-US' , { day : 'numeric' , month : 'long' , year : 'numeric' } ) ;
557+ }
558+ }
559+
560+ const versionLabel = lang === 'pl' ? 'Wersja' : 'Version' ;
561+ const dateLabel = lang === 'pl' ? 'Data wydania' : 'Release Date' ;
562+
563+ badge . innerHTML = `
564+ <svg style="display:inline;vertical-align:middle;margin-right:6px;" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#98ddff" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><polyline points="12 8 12 12 14 14"/></svg>
565+ <strong style="color:var(--accent-blue);">${ versionLabel } : ${ version } </strong>
566+ ${ dateStr ? ` | ${ dateLabel } : ${ dateStr } ` : '' }
567+ ` ;
568+ } )
569+ . catch ( ( ) => {
570+ // Silently hide badge on failure — don't break the page
571+ badge . innerHTML = '' ;
572+ } ) ;
573+ }
574+
399575// --- Decrypt Text Effect ---
400576function decodeText ( element ) {
401577 const originalText = element . getAttribute ( 'data-original' ) || element . innerText ;
@@ -608,6 +784,12 @@ document.addEventListener('DOMContentLoaded', () => {
608784 changeLang ( initialLang ) ;
609785 initScrollAnimations ( ) ;
610786
787+ // Fetch latest GitHub release version
788+ fetchLatestRelease ( ) ;
789+
790+ // Fetch full changelog from GitHub releases
791+ fetchChangelog ( ) ;
792+
611793 // 4. Scroll Progress Bar logic
612794 if ( typeof gsap !== 'undefined' && typeof ScrollTrigger !== 'undefined' ) {
613795 gsap . to ( '#scroll-progress' , {
0 commit comments