사이트 다크 옵시디언 리디자인 + 디자인 감사 폴리시 (redesign/design-taste/high-end 스킬 적용) — 병행 세션의 리디자인(옵시디언 서피스 사다리·머신드 더블베젤·플루이드 아일랜드 내비·히어로 스트림 시뮬레이터·모바일 드로어)을 기반으로 감사 교정: 아이브로우 배급(페이지당 7→1, 장식 점 제거), em대시 전소거(타이틀 3종), 히어로 규율(헤드라인 2줄·서브텍스트 20단어 이내·신뢰 마이크로스트립 제거·CTA 라벨 다운로드/사용 안내로 의도별 단일화), 3등분 카드 파이프라인을 단일 베젤 레일(헤어라인 분할+아이콘 배지)로 재구성, 과장 카피 완화(100%·1초·평균1~3초·초고속), 시뮬레이터에 예시 화면 캡션 명시, 모바일 가로 오버플로 수정(그리드 min-width:0 — 긴 mono URL 이 열 최소폭을 밀던 실결함), OG 메타 3종·skip-link·focus-visible 링·브랜드 404 추가 — 데스크톱 1440/모바일 390 스크린샷 검증, scrollWidth==viewport 확인, 재배포 완료
This commit is contained in:
parent
02995f4153
commit
9c9d9cb024
7 changed files with 2482 additions and 510 deletions
155
site/site.js
155
site/site.js
|
|
@ -1,31 +1,91 @@
|
|||
// MEDIABROWSER 사이트 공용 스크립트
|
||||
// 1) 스크롤 등장(reveal) — IntersectionObserver, 모션 축소 설정이면 즉시 표시
|
||||
// 2) releases.html — releases.json(배포 시 생성)을 읽어 릴리스 목록 렌더
|
||||
// MEDIABROWSER Official Site Script
|
||||
// 1) IntersectionObserver Scroll Reveal
|
||||
// 2) Mobile Navigation Menu Controller
|
||||
// 3) Hero UI Simulator Micro-Interactions
|
||||
// 4) Dynamic Releases Renderer (releases.json)
|
||||
|
||||
// ── 1. Scroll Reveal Controller ──────────────────────────────
|
||||
(function initReveal() {
|
||||
const items = document.querySelectorAll(".reveal");
|
||||
if (!items.length) return;
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches || !("IntersectionObserver" in window)) {
|
||||
return; // .js 게이트를 켜지 않으면 CSS 기본값(표시 상태) 그대로
|
||||
return;
|
||||
}
|
||||
document.documentElement.classList.add("js");
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
|
||||
if (e.isIntersecting) {
|
||||
e.target.classList.add("in");
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
}
|
||||
}, { threshold: 0.15, rootMargin: "0px 0px -8% 0px" });
|
||||
}, { threshold: 0.12, rootMargin: "0px 0px -6% 0px" });
|
||||
|
||||
items.forEach((el) => io.observe(el));
|
||||
})();
|
||||
|
||||
// ── 릴리스 렌더 ──────────────────────────────────────────────
|
||||
// ── 2. Mobile Nav Drawer Controller ──────────────────────────
|
||||
(function initMobileNav() {
|
||||
const toggleBtn = document.getElementById("nav-toggle");
|
||||
const overlay = document.getElementById("mobile-menu");
|
||||
if (!toggleBtn || !overlay) return;
|
||||
|
||||
function toggleMenu() {
|
||||
const isActive = overlay.classList.toggle("is-active");
|
||||
toggleBtn.setAttribute("aria-expanded", String(isActive));
|
||||
document.body.style.overflow = isActive ? "hidden" : "";
|
||||
}
|
||||
|
||||
toggleBtn.addEventListener("click", toggleMenu);
|
||||
|
||||
overlay.querySelectorAll(".mobile-menu-link").forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
overlay.classList.remove("is-active");
|
||||
toggleBtn.setAttribute("aria-expanded", "false");
|
||||
document.body.style.overflow = "";
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
// ── 3. Hero UI Simulator Micro-Animation ─────────────────────
|
||||
(function initHeroSimulator() {
|
||||
const progressEl = document.getElementById("sim-progress");
|
||||
const percentEl = document.getElementById("sim-percent");
|
||||
const speedEl = document.getElementById("sim-speed");
|
||||
if (!progressEl || !percentEl || !speedEl) return;
|
||||
|
||||
let current = 72;
|
||||
let direction = 1;
|
||||
|
||||
setInterval(() => {
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
||||
|
||||
if (direction === 1) {
|
||||
current += 0.8;
|
||||
if (current >= 96) direction = -1;
|
||||
} else {
|
||||
current = 64;
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
const rounded = Math.min(99, Math.floor(current));
|
||||
progressEl.style.width = rounded + "%";
|
||||
percentEl.textContent = rounded + "%";
|
||||
|
||||
const baseSpeed = 18.2 + (Math.random() * 2.4 - 1.2);
|
||||
speedEl.textContent = baseSpeed.toFixed(1) + " MB/s";
|
||||
}, 1200);
|
||||
})();
|
||||
|
||||
// ── 4. Releases Renderer ─────────────────────────────────────
|
||||
const PLATFORMS = [
|
||||
{ id: "windows", label: "Windows", icon: "windows-logo", sub: "설치자를 실행하면 됩니다",
|
||||
{ id: "windows", label: "Windows", icon: "windows-logo", sub: "x64 및 ARM64 네이티브 설치자 (exe)",
|
||||
match: (n) => /setup.*\.exe$/i.test(n) || /win/i.test(n) },
|
||||
{ id: "macos", label: "macOS 프리뷰", icon: "apple-logo", sub: "tar.gz 를 풀어 실행하는 프리뷰 빌드",
|
||||
{ id: "macos", label: "macOS 프리뷰", icon: "apple-logo", sub: "Apple Silicon & Intel tar.gz 프리뷰 빌드",
|
||||
match: (n) => /osx|darwin|mac/i.test(n) },
|
||||
{ id: "linux", label: "Linux 프리뷰", icon: "linux-logo", sub: "tar.gz 를 풀어 실행하는 프리뷰 빌드",
|
||||
{ id: "linux", label: "Linux 프리뷰", icon: "linux-logo", sub: "Ubuntu, Fedora, Arch tar.gz 프리뷰 빌드",
|
||||
match: (n) => /linux/i.test(n) },
|
||||
{ id: "android", label: "Android", icon: "android-logo", sub: "컴패니언 앱 APK",
|
||||
{ id: "android", label: "Android", icon: "android-logo", sub: "스마트폰 로컬 스트리밍 컴패니언 APK",
|
||||
match: (n) => /\.apk$/i.test(n) },
|
||||
];
|
||||
|
||||
|
|
@ -56,8 +116,14 @@ function esc(s) {
|
|||
|
||||
function assetRow(a, recommended) {
|
||||
return `<div class="dl-asset">
|
||||
<div><div class="dl-asset__name">${esc(a.name)}</div><div class="dl-asset__size">${fmtSize(a.size)}</div></div>
|
||||
<a class="btn btn--primary btn--sm ${recommended ? "dl-reco" : ""}" href="${esc(a.url)}" title="${recommended ? "이 기기에 권장" : ""}">받기</a>
|
||||
<div>
|
||||
<div class="dl-asset__name">${esc(a.name)}</div>
|
||||
<div class="dl-asset__size text-mono">${fmtSize(a.size)} ${recommended ? '<span style="color:var(--accent);font-weight:700">· 현재 기기에 권장</span>' : ''}</div>
|
||||
</div>
|
||||
<a class="btn btn--primary btn--sm ${recommended ? "dl-reco" : ""}" href="${esc(a.url)}" title="${recommended ? "현재 기기에 권장" : ""}">
|
||||
<span>다운로드</span>
|
||||
<span class="btn__icon-badge"><svg class="icon" aria-hidden="true"><use href="assets/icons.svg#download-simple"/></svg></span>
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
|
@ -68,64 +134,85 @@ function groupsHtml(assets) {
|
|||
list.forEach((a) => used.add(a.name));
|
||||
if (!list.length) return "";
|
||||
return `<div class="dl-group" id="${p.id}">
|
||||
<div class="dl-group__head"><svg class="icon" aria-hidden="true"><use href="assets/icons.svg#${p.icon}"/></svg>${p.label}</div>
|
||||
<div class="dl-group__head">
|
||||
<svg class="icon" aria-hidden="true"><use href="assets/icons.svg#${p.icon}"/></svg>
|
||||
<span>${p.label}</span>
|
||||
</div>
|
||||
<div class="dl-group__sub">${p.sub}</div>
|
||||
${list.map((a) => assetRow(a, detectPreferredAsset(a.name))).join("")}
|
||||
</div>`;
|
||||
}).filter(Boolean);
|
||||
|
||||
const rest = assets.filter((a) => !used.has(a.name));
|
||||
if (rest.length) {
|
||||
groups.push(`<div class="dl-group"><div class="dl-group__head">기타</div><div class="dl-group__sub">그 밖의 파일</div>
|
||||
${rest.map((a) => assetRow(a, false)).join("")}</div>`);
|
||||
groups.push(`<div class="dl-group">
|
||||
<div class="dl-group__head">기타 아카이브</div>
|
||||
<div class="dl-group__sub">추가 바이너리 및 체크섬 파일</div>
|
||||
${rest.map((a) => assetRow(a, false)).join("")}
|
||||
</div>`);
|
||||
}
|
||||
return `<div class="dl-groups">${groups.join("")}</div>`;
|
||||
}
|
||||
|
||||
function releaseCard(r, featured) {
|
||||
const pill = r.prerelease ? `<span class="rel-pill rel-pill--pre">프리뷰</span>` : `<span class="rel-pill">안정판</span>`;
|
||||
const pill = r.prerelease ? `<span class="rel-pill rel-pill--pre">프리뷰 빌드</span>` : `<span class="rel-pill">공식 안정판</span>`;
|
||||
const meta = `<div class="rel-featured__meta">
|
||||
<span class="rel-featured__tag">${esc(r.name || r.tag)}</span>${pill}
|
||||
<span class="rel-featured__date">${fmtDate(r.published_at)}</span>
|
||||
<span class="rel-featured__tag">${esc(r.name || r.tag)}</span>
|
||||
${pill}
|
||||
<span class="rel-featured__date text-mono">${fmtDate(r.published_at)}</span>
|
||||
</div>
|
||||
${r.notes ? `<p class="rel-featured__notes">${esc(r.notes)}</p>` : ""}
|
||||
${groupsHtml(r.assets)}`;
|
||||
if (featured) return `<article class="rel-featured glass">${meta}</article>`;
|
||||
return `<details><summary><span class="rel-featured__tag" style="font-size:19px">${esc(r.name || r.tag)}</span>${pill}
|
||||
<span class="rel-featured__date">${fmtDate(r.published_at)}</span></summary>
|
||||
<div class="rel-old__body">${r.notes ? `<p class="rel-featured__notes">${esc(r.notes)}</p>` : ""}${groupsHtml(r.assets)}</div>
|
||||
</details>`;
|
||||
|
||||
if (featured) {
|
||||
return `<article class="bezel-shell"><div class="bezel-inner rel-featured">${meta}</div></article>`;
|
||||
}
|
||||
return `<div class="bezel-shell"><div class="bezel-inner"><details>
|
||||
<summary>
|
||||
<span class="rel-featured__tag" style="font-size:1.15rem">${esc(r.name || r.tag)}</span>
|
||||
${pill}
|
||||
<span class="rel-featured__date text-mono">${fmtDate(r.published_at)}</span>
|
||||
<svg class="icon faq-chevron" style="margin-left:8px"><use href="assets/icons.svg#caret-down"/></svg>
|
||||
</summary>
|
||||
<div class="rel-old__body">
|
||||
${r.notes ? `<p class="rel-featured__notes">${esc(r.notes)}</p>` : ""}
|
||||
${groupsHtml(r.assets)}
|
||||
</div>
|
||||
</details></div></div>`;
|
||||
}
|
||||
|
||||
function emptyState() {
|
||||
return `<div class="rel-empty glass">
|
||||
return `<div class="bezel-shell rel-empty">
|
||||
<svg class="icon" aria-hidden="true"><use href="assets/icons.svg#rocket-launch"/></svg>
|
||||
<h2>첫 공개 릴리스를 준비하고 있어요</h2>
|
||||
<p>빌드 파이프라인은 이미 돌아가고 있습니다. 곧 이 자리에서 설치자를 받을 수 있습니다.
|
||||
먼저 <a href="guide.html" style="color:var(--accent);font-weight:700;text-decoration:none">사용 안내</a>를 둘러보세요.</p>
|
||||
<h2>첫 공개 릴리스를 준비하고 있습니다</h2>
|
||||
<p style="color:var(--text-secondary);max-width:54ch">빌드 파이프라인이 정상 작동 중이며 곧 공식 설치자가 배포됩니다. 먼저 <a href="guide.html" style="color:var(--accent);font-weight:700">사용 안내</a>를 둘러보세요.</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
(async function initReleases() {
|
||||
const root = document.getElementById("release-root");
|
||||
if (!root) return;
|
||||
|
||||
try {
|
||||
const res = await fetch("releases.json", { cache: "no-cache" });
|
||||
if (!res.ok) throw new Error("HTTP " + res.status);
|
||||
const data = await res.json();
|
||||
const releases = (data.releases || []);
|
||||
if (!releases.length) { root.innerHTML = emptyState(); return; }
|
||||
if (!releases.length) {
|
||||
root.innerHTML = emptyState();
|
||||
return;
|
||||
}
|
||||
const [latest, ...older] = releases;
|
||||
let html = releaseCard(latest, true);
|
||||
if (older.length) {
|
||||
html += `<div class="rel-old"><h2 style="font-size:20px;margin-top:14px">이전 버전</h2>
|
||||
html += `<div class="rel-old"><h2 style="font-size:1.35rem;margin-bottom:14px">이전 버전 기록</h2>
|
||||
${older.map((r) => releaseCard(r, false)).join("")}</div>`;
|
||||
}
|
||||
root.innerHTML = html;
|
||||
} catch (err) {
|
||||
root.innerHTML = `<div class="rel-empty glass">
|
||||
<h2>목록을 불러오지 못했어요</h2>
|
||||
<p>잠시 후 새로고침해 주세요. 급하다면
|
||||
<a href="https://git.chanpaca.net/Video-Downloader/releases" style="color:var(--accent);font-weight:700;text-decoration:none" rel="noopener">릴리스 저장소</a>에서 직접 받을 수 있습니다.</p>
|
||||
root.innerHTML = `<div class="bezel-shell rel-empty">
|
||||
<h2>릴리스 목록을 불러오지 못했습니다</h2>
|
||||
<p style="color:var(--text-secondary)">네트워크 상태를 확인하고 새로고침해 주세요. 급하신 경우 <a href="https://git.chanpaca.net/Video-Downloader/releases" style="color:var(--accent);font-weight:700" target="_blank" rel="noopener">릴리스 저장소</a>에서 직접 다운로드하실 수 있습니다.</p>
|
||||
</div>`;
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue