diff --git a/site/site-v6-core.js b/site/site-v6-core.js index cc6f3e7..5d9511e 100644 --- a/site/site-v6-core.js +++ b/site/site-v6-core.js @@ -7,169 +7,66 @@ const REPOSITORY = 'masarray/vst-enhancer'; const RELEASES_ROOT = `https://github.com/${REPOSITORY}/releases`; const RELEASE_FALLBACK = `${RELEASES_ROOT}/latest`; - const LATEST_API = `https://api.github.com/repos/${REPOSITORY}/releases/latest`; - const OFFICIAL_RELEASE_PATH = `/${REPOSITORY}/releases`; - const OFFICIAL_DOWNLOAD_PREFIX = `/${REPOSITORY}/releases/download/`; const VERSION_RE = /^v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/; const copy = language === 'id' ? { enabled: 'Rilis resmi tersedia', paused: 'Unduhan resmi sedang dijeda', - error: 'Status rilis tidak dapat diperiksa', - installer: 'Unduh gratis untuk Windows', - installerShort: 'Unduh gratis', - macHero: 'Unduh gratis untuk Mac', - eyebrow: 'Rilis terbaru', - heading: 'Apa yang meningkat di', - notes: 'Buka catatan rilis lengkap', - published: 'Diterbitkan', - liveSource: 'Data langsung GitHub Releases', - reviewedSource: 'Metadata rilis tervalidasi', - fallbackHighlight: 'Catatan peningkatan lengkap tersedia di halaman GitHub Release resmi.' + error: 'Status rilis tidak dapat diperiksa' } : { enabled: 'Official release available', paused: 'Official download is temporarily paused', - error: 'Release status could not be checked', - installer: 'Download free for Windows', - installerShort: 'Download free', - macHero: 'Download free for Mac', - eyebrow: 'Latest release', - heading: 'What improved in', - notes: 'Open full release notes', - published: 'Published', - liveSource: 'Live GitHub Releases data', - reviewedSource: 'Reviewed release metadata', - fallbackHighlight: 'The complete improvement notes are available on the official GitHub Release page.' + error: 'Release status could not be checked' }; - const officialReleaseUrl = (value, asset = false) => { - if (typeof value !== 'string' || value.length > 800) return null; + const officialReleaseUrl = (value, version, asset = false) => { + if (typeof value !== 'string' || value.length > 800 || !VERSION_RE.test(version || '')) return null; try { const url = new URL(value); - const validPath = url.pathname === OFFICIAL_RELEASE_PATH || url.pathname.startsWith(`${OFFICIAL_RELEASE_PATH}/`); - if (url.protocol !== 'https:' || url.hostname !== 'github.com' || !validPath) return null; - if (asset && !url.pathname.startsWith(OFFICIAL_DOWNLOAD_PREFIX)) return null; - return url.href; + if (url.protocol !== 'https:' || url.hostname !== 'github.com') return null; + const releasePath = `/${REPOSITORY}/releases/tag/${version}`; + const assetPrefix = `/${REPOSITORY}/releases/download/${version}/`; + const valid = asset ? url.pathname.startsWith(assetPrefix) : url.pathname === releasePath; + return valid ? url.href : null; } catch (_) { return null; } }; - const officialAsset = (asset) => { - if (!asset || typeof asset.name !== 'string' || typeof asset.browser_download_url !== 'string') return null; - if (asset.state && asset.state !== 'uploaded') return null; - const url = officialReleaseUrl(asset.browser_download_url, true); - return url ? { name: asset.name, url } : null; - }; + const parseManifest = (payload) => { + if (!payload || typeof payload !== 'object') return null; + const version = String(payload.version || ''); + if (!VERSION_RE.test(version) || Number(payload.schemaVersion || 0) < 3) return null; - const scoreInstaller = (name) => { - const lower = name.toLowerCase(); - if (!lower.endsWith('.exe')) return -1; - let score = 0; - if (lower.includes('arsonkupik')) score += 20; - if (lower.includes('windows')) score += 10; - if (lower.includes('x64') || lower.includes('win64')) score += 8; - if (lower.includes('setup')) score += 12; - if (lower.includes('installer')) score += 10; - if (lower.includes('portable')) score -= 30; - if (lower.includes('activator') || lower.includes('key')) score -= 100; - return score; - }; + const platforms = new Set(Array.isArray(payload.platforms) ? payload.platforms : []); + if (!platforms.has('windows-x64') || !platforms.has('macos-universal')) return null; - const chooseAsset = (assets, predicate, scorer = () => 0) => assets - .filter((asset) => predicate(asset.name.toLowerCase())) - .sort((a, b) => scorer(b.name) - scorer(a.name))[0] || null; + const releaseUrl = officialReleaseUrl(payload.releaseUrl, version, false); + if (!releaseUrl) return null; - const cleanMarkdown = (value) => String(value || '') - .replace(/!\[[^\]]*\]\([^)]*\)/g, '') - .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') - .replace(/[`*_~>#]/g, '') - .replace(/<[^>]+>/g, '') - .replace(/\s+/g, ' ') - .trim(); + const asset = (key) => officialReleaseUrl(payload[key], version, true); + const enabled = payload.distributionEnabled === true && payload.distributionStatus !== 'paused'; + const installerUrl = asset('installerUrl'); - const extractHighlights = (body, maximum = 6) => { - if (typeof body !== 'string') return []; - const ignored = /^(full changelog|contributors?|new contributors?|assets?|checksums?|sha-?256)\b/i; - const unique = new Set(); - const highlights = []; - const add = (value) => { - const cleaned = cleanMarkdown(value).replace(/^[-+*]\s+/, '').replace(/^\d+[.)]\s+/, ''); - const key = cleaned.toLowerCase(); - if (!cleaned || cleaned.length < 8 || ignored.test(cleaned) || unique.has(key)) return; - unique.add(key); - highlights.push(cleaned.length > 220 ? `${cleaned.slice(0, 217).trim()}…` : cleaned); - }; + if (enabled && !installerUrl) return null; - body.split(/\r?\n/).forEach((line) => { - const trimmed = line.trim(); - if (/^[-+*]\s+/.test(trimmed) || /^\d+[.)]\s+/.test(trimmed)) add(trimmed); - }); - if (!highlights.length) { - body.split(/\r?\n\s*\r?\n/).forEach((paragraph) => { - const trimmed = paragraph.trim(); - if (!trimmed.startsWith('#')) add(trimmed); - }); - } - return highlights.slice(0, maximum); - }; - - const parseGitHubRelease = (payload) => { - if (!payload || payload.draft === true || payload.prerelease === true) return null; - if (typeof payload.tag_name !== 'string' || !VERSION_RE.test(payload.tag_name)) return null; - const releaseUrl = officialReleaseUrl(payload.html_url); - const assets = Array.isArray(payload.assets) ? payload.assets.map(officialAsset).filter(Boolean) : []; - const installer = chooseAsset(assets, (name) => name.endsWith('.exe'), scoreInstaller); - if (!releaseUrl || !installer || scoreInstaller(installer.name) < 20) return null; return { - type: 'enabled', - source: 'github-latest-api', - version: payload.tag_name, - releaseName: cleanMarkdown(payload.name || payload.tag_name), - publishedAt: payload.published_at || payload.created_at || null, + type: enabled ? 'enabled' : 'paused', + source: 'local-release-manifest', + version, releaseUrl, - installerUrl: installer.url, - installerName: installer.name, - vst3Url: chooseAsset(assets, (name) => name.endsWith('.zip') && name.includes('windows') && name.includes('vst3'))?.url || null, - standaloneUrl: chooseAsset(assets, (name) => name.endsWith('.zip') && name.includes('windows') && name.includes('standalone'))?.url || null, - macDmgUrl: chooseAsset(assets, (name) => name.endsWith('.dmg') && name.includes('macos'))?.url || null, - macVst3Url: chooseAsset(assets, (name) => name.endsWith('.zip') && name.includes('macos') && name.includes('vst3'))?.url || null, - macStandaloneUrl: chooseAsset(assets, (name) => name.endsWith('.zip') && name.includes('macos') && name.includes('standalone'))?.url || null, - checksumsUrl: chooseAsset(assets, (name) => name === 'sha256sums.txt')?.url || null, - highlights: extractHighlights(payload.body) + installerUrl, + vst3Url: asset('vst3Url'), + standaloneUrl: asset('standaloneUrl'), + macDmgUrl: asset('macDmgUrl'), + macVst3Url: asset('macVst3Url'), + macStandaloneUrl: asset('macStandaloneUrl'), + checksumsUrl: asset('checksumsUrl') }; }; - const parseManifest = (release) => { - if (!release || typeof release !== 'object' || !VERSION_RE.test(String(release.version || ''))) return null; - const releaseUrl = officialReleaseUrl(release.releaseUrl) || RELEASE_FALLBACK; - const installerUrl = officialReleaseUrl(release.installerUrl, true); - const highlights = Array.isArray(release.releaseHighlights) - ? release.releaseHighlights.map(cleanMarkdown).filter(Boolean).slice(0, 6) - : []; - return release.distributionEnabled === true && installerUrl - ? { - type: 'enabled', - source: 'local-release-manifest', - version: release.version, - releaseName: cleanMarkdown(release.releaseName || release.version), - publishedAt: release.publishedAt || null, - releaseUrl, - installerUrl, - installerName: decodeURIComponent(new URL(installerUrl).pathname.split('/').pop()), - vst3Url: officialReleaseUrl(release.vst3Url, true), - standaloneUrl: officialReleaseUrl(release.standaloneUrl, true), - macDmgUrl: officialReleaseUrl(release.macDmgUrl, true), - macVst3Url: officialReleaseUrl(release.macVst3Url, true), - macStandaloneUrl: officialReleaseUrl(release.macStandaloneUrl, true), - checksumsUrl: officialReleaseUrl(release.checksumsUrl, true), - highlights - } - : { type: 'paused', source: 'local-release-manifest', version: release.version, releaseUrl, highlights }; - }; - const setLink = (element, href, enabled = true) => { if (!element) return; if (enabled && href) { @@ -177,243 +74,87 @@ element.removeAttribute('aria-disabled'); element.removeAttribute('data-release-pending'); } else { - element.href = '#download'; + element.href = RELEASE_FALLBACK; element.setAttribute('aria-disabled', 'true'); element.setAttribute('data-release-pending', 'true'); } }; - const lockDirectDownloadCtas = () => { - document.querySelectorAll('[data-installer-cta]').forEach((button) => setLink(button, null, false)); - ['vst3-link', 'standalone-link', 'mac-dmg-link', 'mac-vst3-link', 'mac-standalone-link'] - .forEach((id) => setLink(document.getElementById(id), null, false)); - }; - - const updateStructuredData = (release) => { - const script = document.getElementById('software-structured-data'); - if (!script || !release) return; - try { - const data = JSON.parse(script.textContent); - const software = data['@graph']?.find((entry) => entry['@type'] === 'SoftwareApplication'); - if (!software) return; - software.softwareVersion = release.version?.replace(/^v/i, '') || software.softwareVersion; - software.downloadUrl = release.installerUrl || release.releaseUrl; - software.releaseNotes = release.releaseUrl; - if (software.offers) software.offers.url = release.installerUrl || release.releaseUrl; - script.textContent = JSON.stringify(data, null, 2); - } catch (_) { - // Keep the reviewed static JSON-LD when runtime enhancement fails. - } - }; - - const ensureReleasePanelStyles = () => { - if (document.getElementById('latest-release-panel-style')) return; - const style = document.createElement('style'); - style.id = 'latest-release-panel-style'; - style.textContent = ` - .release-update-section{padding-top:clamp(42px,6vw,74px);padding-bottom:clamp(42px,6vw,74px)} - .release-update-shell{display:grid;grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr);gap:clamp(24px,4vw,56px);align-items:start;padding:clamp(22px,3vw,34px);border:1px solid rgba(255,255,255,.11);border-radius:22px;background:linear-gradient(135deg,rgba(255,255,255,.055),rgba(255,255,255,.018));box-shadow:0 18px 50px rgba(0,0,0,.18)} - .release-update-copy h2{margin:.45rem 0 .7rem;font-size:clamp(1.65rem,3vw,2.5rem);line-height:1.08;font-weight:650;letter-spacing:-.03em} - .release-update-copy p{margin:0;color:var(--muted,#b7b2c2);font-size:.94rem} - .release-update-list{display:grid;gap:10px;margin:0;padding:0;list-style:none} - .release-update-list li{position:relative;padding:12px 14px 12px 38px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(0,0,0,.14);font-size:.96rem;line-height:1.48} - .release-update-list li::before{content:'↗';position:absolute;left:14px;top:11px;color:#d58cff;font-weight:700} - .release-update-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:18px} - .release-update-source{font-size:.78rem;color:var(--muted,#9892a4)} - .hero-mac-download svg{width:1em;height:1em;flex:0 0 1em;fill:currentColor} - @media (max-width:760px){.release-update-shell{grid-template-columns:1fr;border-radius:18px;padding:20px}.release-update-copy h2{font-size:1.65rem}.release-update-list li{font-size:.91rem}} - `; - document.head.appendChild(style); - }; - - const ensureReleasePanel = () => { - let section = document.getElementById('latest-release'); - if (section) return section; - ensureReleasePanelStyles(); - section = document.createElement('section'); - section.id = 'latest-release'; - section.className = 'section release-update-section'; - section.setAttribute('aria-labelledby', 'latest-release-title'); - section.innerHTML = ` -
${isId - ? 'Universal untuk Apple Silicon dan Intel. Ad-hoc signed, tanpa Developer ID dan tanpa notarization.' - : 'Universal for Apple Silicon and Intel. Ad-hoc signed, without Developer ID signing or notarization.'}
- ${isId ? 'Unduh DMG Mac' : 'Download Mac DMG'} -