Files

218 lines
9.3 KiB
JavaScript
Raw Normal View History

(function() {
'use strict';
/* --- Header scroll effect --- */
var header = document.getElementById('siteHeader');
var backTop = document.querySelector('.back-top');
var scrollThreshold = 40;
function onScroll() {
if (window.scrollY > scrollThreshold) {
header.classList.add('scrolled');
} else {
header.classList.remove('scrolled');
}
if (backTop) {
backTop.classList.toggle('visible', window.scrollY > 480);
}
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
if (backTop) {
backTop.addEventListener('click', function() {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
/* --- Mobile menu --- */
var menuBtn = document.querySelector('.menu-btn');
var mobileMenu = document.getElementById('mobileMenu');
if (menuBtn && mobileMenu) {
menuBtn.addEventListener('click', function() {
var isOpen = mobileMenu.classList.toggle('open');
menuBtn.setAttribute('aria-expanded', String(isOpen));
var spans = menuBtn.querySelectorAll('span');
if (isOpen) {
spans[0].style.transform = 'translateY(4px) rotate(45deg)';
spans[1].style.transform = 'translateY(-4px) rotate(-45deg)';
} else {
spans[0].style.transform = '';
spans[1].style.transform = '';
}
});
mobileMenu.querySelectorAll('a').forEach(function(link) {
link.addEventListener('click', function() {
mobileMenu.classList.remove('open');
menuBtn.setAttribute('aria-expanded', 'false');
var spans = menuBtn.querySelectorAll('span');
spans[0].style.transform = '';
spans[1].style.transform = '';
});
});
}
/* --- Downloads use the same release feed as desktop updates. --- */
var updateFeedURL = 'https://api.gitcode.com/api/v5/repos/wendao-ai/wendao-ai-updates/raw/updates/stable.json';
var releasePageURL = 'https://gitcode.com/wendao-ai/wendao-ai-updates/releases';
function formatSize(bytes) {
if (!Number.isFinite(bytes) || bytes <= 0) return '';
return Math.round(bytes / 1024 / 1024) + ' MB';
}
function detectedPlatform() {
var agent = navigator.userAgent || '';
if (/Windows/i.test(agent)) return { platform: 'windows', fallbackArch: 'amd64' };
if (/Macintosh|Mac OS X/i.test(agent)) return { platform: 'macos', fallbackArch: 'arm64' };
return null;
}
async function detectedPackageKey(packages) {
var detected = detectedPlatform();
if (!detected) return '';
var arch = detected.fallbackArch;
if (navigator.userAgentData && navigator.userAgentData.getHighEntropyValues) {
try {
var values = await navigator.userAgentData.getHighEntropyValues(['architecture']);
if (/arm/i.test(values.architecture || '')) arch = 'arm64';
if (/x86|x64|amd64/i.test(values.architecture || '')) arch = 'amd64';
} catch (error) {
// The platform fallback remains usable when the browser hides architecture.
}
}
var key = detected.platform + '/' + arch;
return packages[key] ? key : '';
}
function setDownloadFailure() {
var selectedLink = document.getElementById('downloadSelected');
if (selectedLink) selectedLink.setAttribute('href', releasePageURL);
document.querySelectorAll('.js-download').forEach(function(link) { link.setAttribute('href', releasePageURL); });
var note = document.getElementById('downloadVersion');
if (note) note.textContent = '暂时无法获取最新版本,请前往发布页下载';
var extensionVersion = document.querySelector('[data-extension-version]');
if (extensionVersion) extensionVersion.textContent = '系统更新源暂时不可用,无法获取插件下载地址';
}
async function loadDownloads() {
try {
var response = await fetch(updateFeedURL, { headers: { Accept: 'application/json' } });
if (!response.ok) throw new Error('HTTP ' + response.status);
var feed = await response.json();
if (feed.product !== 'wendao-ai' || !feed.version || !feed.packages) throw new Error('Invalid update feed');
var browserExtensions = feed.browserExtensions;
var extensionPackages = browserExtensions && browserExtensions.packages;
['chrome', 'edge', 'safari', 'firefox'].forEach(function(browser) {
var link = document.querySelector('[data-extension-browser="' + browser + '"]');
var item = extensionPackages && extensionPackages[browser];
if (!link || !item || !/^https:\/\//.test(item.url || '')) return;
link.setAttribute('href', item.url);
link.removeAttribute('aria-disabled');
link.classList.remove('is-disabled');
var browserNames = { chrome: 'Chrome', edge: 'Edge', safari: 'Safari', firefox: 'Firefox' };
link.innerHTML = '下载 ' + browserNames[browser] + ' 版 <span class="arrow">&darr;</span>';
});
var extensionVersion = document.querySelector('[data-extension-version]');
if (extensionVersion) {
extensionVersion.textContent = browserExtensions && browserExtensions.version
? '当前插件版本 v' + browserExtensions.version + ' \u00b7 支持 Chrome / Edge / Safari / Firefox'
: '系统更新源暂未提供浏览器插件下载';
}
var note = document.getElementById('downloadVersion');
var packageKey = await detectedPackageKey(feed.packages);
var picker = document.getElementById('downloadPackage');
var selectedLink = document.getElementById('downloadSelected');
if (picker && packageKey) picker.value = packageKey;
function applySelectedPackage() {
var key = picker ? picker.value : packageKey;
var selected = key && feed.packages[key];
var url = selected && /^https:\/\//.test(selected.url || '') ? selected.url : releasePageURL;
var size = selected ? formatSize(Number(selected.size)) : '';
if (selectedLink) selectedLink.setAttribute('href', url);
document.querySelectorAll('.js-download').forEach(function(link) {
link.setAttribute('href', url);
link.removeAttribute('download');
});
if (note) note.textContent = '当前版本 v' + feed.version + (size ? ' \u00b7 安装包 ' + size : '') + ' \u00b7 已选择 ' + picker.options[picker.selectedIndex].text;
}
if (picker) picker.addEventListener('change', applySelectedPackage);
applySelectedPackage();
} catch (error) {
setDownloadFailure();
}
}
loadDownloads();
/* Stable deep-link positioning for campaign previews and screenshot QA. */
if (new URLSearchParams(window.location.search).get('view') === 'workflow') {
var workflowSection = document.getElementById('workflow');
if (workflowSection) {
workflowSection.querySelectorAll('.reveal').forEach(function(item) { item.classList.add('visible'); });
var main = document.querySelector('main');
if (main) main.prepend(workflowSection);
}
}
/* --- Product screenshot preview --- */
var screenDialog = document.getElementById('screenDialog');
if (screenDialog && typeof screenDialog.showModal === 'function') {
var dialogImage = screenDialog.querySelector('img');
var dialogCaption = screenDialog.querySelector('figcaption');
var dialogClose = screenDialog.querySelector('.screen-dialog-close');
document.querySelectorAll('.js-screen-open').forEach(function(button) {
button.addEventListener('click', function() {
var source = button.getAttribute('data-screen') || '';
var caption = button.getAttribute('data-caption') || '文到AI真实产品界面';
if (dialogImage) {
dialogImage.setAttribute('src', source);
dialogImage.setAttribute('alt', caption);
}
if (dialogCaption) dialogCaption.textContent = caption;
screenDialog.showModal();
});
});
if (dialogClose) dialogClose.addEventListener('click', function() { screenDialog.close(); });
screenDialog.addEventListener('click', function(event) {
if (event.target === screenDialog) screenDialog.close();
});
}
/* --- Scroll reveal --- */
if ('IntersectionObserver' in window && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
var revealItems = document.querySelectorAll('.reveal');
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
revealItems.forEach(function(item) { observer.observe(item); });
} else {
document.querySelectorAll('.reveal').forEach(function(item) {
item.classList.add('visible');
});
}
/* --- Smooth scroll for anchor links --- */
document.querySelectorAll('a[href^="#"]').forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
var targetId = this.getAttribute('href');
if (!targetId || targetId.charAt(0) !== '#' || targetId === '#') return;
var target = document.querySelector(targetId);
if (target) {
e.preventDefault();
var headerHeight = header ? header.offsetHeight : 0;
var targetPos = target.getBoundingClientRect().top + window.scrollY - headerHeight - 20;
window.scrollTo({ top: targetPos, behavior: 'smooth' });
}
});
});
})();