MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 16: | Line 16: | ||
} catch (e) {} | } catch (e) {} | ||
var pageName = mw.config.get("wgPageName"); | var pageName = mw.config.get("wgPageName"); | ||
var isSysop = (mw.config.get("wgUserGroups") || []).indexOf("sysop") !== -1; | var isSysop = (mw.config.get("wgUserGroups") || []).indexOf("sysop") !== -1; | ||
| Line 340: | Line 339: | ||
sp(subbar, "width", "100%"); | sp(subbar, "width", "100%"); | ||
var hbBtn = document.createElement("button"); | var hbBtn = document.createElement("button"); | ||
sp(hbBtn, "display", "flex"); | sp(hbBtn, "display", "flex"); | ||
| Line 372: | Line 370: | ||
hbBtn.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); }); | hbBtn.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); }); | ||
var searchWrap = document.createElement("div"); | var searchWrap = document.createElement("div"); | ||
sp(searchWrap, "position", "relative"); | sp(searchWrap, "position", "relative"); | ||
| Line 490: | Line 487: | ||
subbar.appendChild(searchWrap); | subbar.appendChild(searchWrap); | ||
if (isSysop) { | if (isSysop) { | ||
var createDocBtn = document.createElement("a"); | var createDocBtn = document.createElement("a"); | ||
| Line 571: | Line 567: | ||
{ | { | ||
items: [ | items: [ | ||
{ label: "Policies & Guidelines", | { label: "Policies & Guidelines", href: "/index.php?title=Erakh_Codex:Policies" }, | ||
], | ], | ||
gold: false | gold: false | ||
| Line 601: | Line 597: | ||
function toggleDrawer() { | function toggleDrawer() { | ||
drawerOpen = !drawerOpen; | drawerOpen = !drawerOpen; | ||
drawer.style.setProperty("display", | drawer.style.setProperty("display", drawerOpen ? "block" : "none", "important"); | ||
drawerOverlay.style.setProperty("display", | drawerOverlay.style.setProperty("display", drawerOpen ? "block" : "none", "important"); | ||
} | } | ||
hbBtn.addEventListener("click", toggleDrawer); | hbBtn.addEventListener("click", toggleDrawer); | ||
| Line 651: | Line 647: | ||
function buildLayout() { | function buildLayout() { | ||
// | // 메인페이지 / Create 페이지에서는 레이아웃 빌드 스킵 | ||
if (pageName === "Erakh_Codex:Create") return; | if (pageName === "Erakh_Codex:Create") return; | ||
if (pageName === "Main_Page") return; | |||
var content = document.querySelector(".mw-body") || document.getElementById("content"); | var content = document.querySelector(".mw-body") || document.getElementById("content"); | ||
| Line 735: | Line 732: | ||
} | } | ||
// ─── | // ─── 문서 생성 페이지 UI ─── | ||
function buildCreatePage() { | function buildCreatePage() { | ||
if (pageName !== "Erakh_Codex:Create") return; | if (pageName !== "Erakh_Codex:Create") return; | ||
| Line 752: | Line 749: | ||
]; | ]; | ||
var firstHeading = document.getElementById("firstHeading"); | var firstHeading = document.getElementById("firstHeading"); | ||
if (firstHeading) sp(firstHeading, "display", "none"); | if (firstHeading) sp(firstHeading, "display", "none"); | ||
| Line 759: | Line 755: | ||
var noArticle = document.querySelector(".noarticletext"); | var noArticle = document.querySelector(".noarticletext"); | ||
if (noArticle) sp(noArticle, "display", "none"); | if (noArticle) sp(noArticle, "display", "none"); | ||
var pViews = document.getElementById("p-views"); | var pViews = document.getElementById("p-views"); | ||
if (pViews) sp(pViews, "display", "none"); | if (pViews) sp(pViews, "display", "none"); | ||
| Line 768: | Line 763: | ||
if (!target) return; | if (!target) return; | ||
if (!isSysop) { | if (!isSysop) { | ||
var denied = document.createElement("div"); | var denied = document.createElement("div"); | ||
| Line 783: | Line 777: | ||
var selectedCat = null; | var selectedCat = null; | ||
var wrap = document.createElement("div"); | var wrap = document.createElement("div"); | ||
sp(wrap, "max-width", "680px"); | sp(wrap, "max-width", "680px"); | ||
| Line 792: | Line 785: | ||
sp(wrap, "box-sizing", "border-box"); | sp(wrap, "box-sizing", "border-box"); | ||
var heading = document.createElement("h2"); | var heading = document.createElement("h2"); | ||
heading.textContent = "Create New Document"; | heading.textContent = "Create New Document"; | ||
| Line 803: | Line 795: | ||
wrap.appendChild(heading); | wrap.appendChild(heading); | ||
var catLabel = document.createElement("p"); | var catLabel = document.createElement("p"); | ||
catLabel.textContent = "Select Category"; | catLabel.textContent = "Select Category"; | ||
| Line 815: | Line 806: | ||
wrap.appendChild(catLabel); | wrap.appendChild(catLabel); | ||
var grid = document.createElement("div"); | var grid = document.createElement("div"); | ||
sp(grid, "display", "grid"); | sp(grid, "display", "grid"); | ||
| Line 876: | Line 866: | ||
wrap.appendChild(grid); | wrap.appendChild(grid); | ||
var chronicleNotice = document.createElement("p"); | var chronicleNotice = document.createElement("p"); | ||
chronicleNotice.textContent = "Chronicle documents use a timeline format. No infobox template will be applied."; | chronicleNotice.textContent = "Chronicle documents use a timeline format. No infobox template will be applied."; | ||
| Line 887: | Line 876: | ||
wrap.appendChild(chronicleNotice); | wrap.appendChild(chronicleNotice); | ||
var divider = document.createElement("div"); | var divider = document.createElement("div"); | ||
sp(divider, "border-top", "1px solid #e2d9c8"); | sp(divider, "border-top", "1px solid #e2d9c8"); | ||
| Line 893: | Line 881: | ||
wrap.appendChild(divider); | wrap.appendChild(divider); | ||
var titleLabel = document.createElement("p"); | var titleLabel = document.createElement("p"); | ||
titleLabel.textContent = "Document Title"; | titleLabel.textContent = "Document Title"; | ||
| Line 905: | Line 892: | ||
wrap.appendChild(titleLabel); | wrap.appendChild(titleLabel); | ||
var titleInput = document.createElement("input"); | var titleInput = document.createElement("input"); | ||
titleInput.type = "text"; | titleInput.type = "text"; | ||
| Line 925: | Line 911: | ||
wrap.appendChild(titleInput); | wrap.appendChild(titleInput); | ||
var createBtn = document.createElement("button"); | var createBtn = document.createElement("button"); | ||
createBtn.textContent = "Create Article"; | createBtn.textContent = "Create Article"; | ||
| Line 970: | Line 955: | ||
} | } | ||
} | } | ||
} | |||
// ─── 메인페이지 빌드 ─── | |||
function buildMainPage() { | |||
if (pageName !== "Main_Page") return; | |||
// MW 기본 콘텐츠 숨김 | |||
var firstHeading = document.getElementById("firstHeading"); | |||
if (firstHeading) sp(firstHeading, "display", "none"); | |||
var parseOutput = document.querySelector(".mw-parser-output"); | |||
if (parseOutput) sp(parseOutput, "display", "none"); | |||
var target = document.getElementById("mw-content-text"); | |||
if (!target) return; | |||
var CATS = [ | |||
{ label: "Chronicle", icon: "ti-clock", href: "/index.php?title=Category:Chronicle" }, | |||
{ label: "Events", icon: "ti-sword", href: "/index.php?title=Category:Events" }, | |||
{ label: "Nations", icon: "ti-building", href: "/index.php?title=Category:Nations" }, | |||
{ label: "Regions", icon: "ti-map", href: "/index.php?title=Category:Regions" }, | |||
{ label: "Characters", icon: "ti-user", href: "/index.php?title=Category:Characters" }, | |||
{ label: "Races", icon: "ti-dna", href: "/index.php?title=Category:Races" }, | |||
{ label: "Organizations", icon: "ti-users", href: "/index.php?title=Category:Organizations" }, | |||
{ label: "Disciplines", icon: "ti-book", href: "/index.php?title=Category:Disciplines" }, | |||
{ label: "Culture", icon: "ti-music", href: "/index.php?title=Category:Culture" }, | |||
{ label: "Artifacts", icon: "ti-diamond", href: "/index.php?title=Category:Artifacts" }, | |||
]; | |||
// Tabler Icons 로드 | |||
var tablerLink = document.createElement("link"); | |||
tablerLink.rel = "stylesheet"; | |||
tablerLink.href = "https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css"; | |||
document.head.appendChild(tablerLink); | |||
// ── 래퍼 | |||
var wrap = document.createElement("div"); | |||
sp(wrap, "max-width", "860px"); | |||
sp(wrap, "margin", "0 auto"); | |||
sp(wrap, "padding", "2.5rem 2rem 4rem"); | |||
sp(wrap, "font-family", "'Inter', sans-serif"); | |||
sp(wrap, "box-sizing", "border-box"); | |||
// ── Hero | |||
var hero = document.createElement("div"); | |||
sp(hero, "background", "#f0ebe0"); | |||
sp(hero, "border", "1px solid #e2d9c8"); | |||
sp(hero, "border-radius", "6px"); | |||
sp(hero, "padding", "2rem 2.5rem"); | |||
sp(hero, "margin-bottom", "2.5rem"); | |||
sp(hero, "display", "flex"); | |||
sp(hero, "align-items", "center"); | |||
sp(hero, "justify-content", "space-between"); | |||
sp(hero, "box-sizing", "border-box"); | |||
var heroText = document.createElement("div"); | |||
var heroTitle = document.createElement("h1"); | |||
heroTitle.textContent = "Erakh Codex"; | |||
sp(heroTitle, "font-size", "1.5rem"); | |||
sp(heroTitle, "font-weight", "600"); | |||
sp(heroTitle, "color", "#2a2218"); | |||
sp(heroTitle, "letter-spacing", "-0.01em"); | |||
sp(heroTitle, "margin", "0 0 0.5rem 0"); | |||
sp(heroTitle, "font-family", "'Inter', sans-serif"); | |||
var heroDesc = document.createElement("p"); | |||
heroDesc.textContent = "The official encyclopedia of the Mirtess Chronicle. Explore the world, its history, peoples, and events — all in one place."; | |||
sp(heroDesc, "font-size", "0.8125rem"); | |||
sp(heroDesc, "color", "#6b5e4e"); | |||
sp(heroDesc, "line-height", "1.7"); | |||
sp(heroDesc, "margin", "0"); | |||
sp(heroDesc, "max-width", "480px"); | |||
sp(heroDesc, "font-family", "'Inter', sans-serif"); | |||
heroText.appendChild(heroTitle); | |||
heroText.appendChild(heroDesc); | |||
var heroStat = document.createElement("div"); | |||
sp(heroStat, "text-align", "center"); | |||
sp(heroStat, "border-left", "1px solid #c8b99a"); | |||
sp(heroStat, "padding-left", "2.5rem"); | |||
sp(heroStat, "flex-shrink", "0"); | |||
var statNum = document.createElement("div"); | |||
statNum.textContent = "—"; | |||
sp(statNum, "font-size", "2rem"); | |||
sp(statNum, "font-weight", "600"); | |||
sp(statNum, "color", "#c8a060"); | |||
sp(statNum, "letter-spacing", "-0.02em"); | |||
sp(statNum, "font-family", "'Inter', sans-serif"); | |||
var statLbl = document.createElement("div"); | |||
statLbl.textContent = "Documents"; | |||
sp(statLbl, "font-size", "0.6875rem"); | |||
sp(statLbl, "color", "#9c8e80"); | |||
sp(statLbl, "text-transform", "uppercase"); | |||
sp(statLbl, "letter-spacing", "0.08em"); | |||
sp(statLbl, "margin-top", "0.25rem"); | |||
sp(statLbl, "font-family", "'Inter', sans-serif"); | |||
heroStat.appendChild(statNum); | |||
heroStat.appendChild(statLbl); | |||
hero.appendChild(heroText); | |||
hero.appendChild(heroStat); | |||
wrap.appendChild(hero); | |||
// ── 섹션 타이틀 헬퍼 | |||
function makeSectionTitle(text) { | |||
var t = document.createElement("div"); | |||
t.textContent = text; | |||
sp(t, "font-size", "0.6875rem"); | |||
sp(t, "font-weight", "500"); | |||
sp(t, "color", "#9c8e80"); | |||
sp(t, "text-transform", "uppercase"); | |||
sp(t, "letter-spacing", "0.1em"); | |||
sp(t, "margin-bottom", "1rem"); | |||
sp(t, "padding-bottom", "0.4rem"); | |||
sp(t, "border-bottom", "1px solid #e2d9c8"); | |||
sp(t, "font-family", "'Inter', sans-serif"); | |||
return t; | |||
} | |||
// ── 카테고리 그리드 | |||
wrap.appendChild(makeSectionTitle("Browse by Category")); | |||
var catGrid = document.createElement("div"); | |||
sp(catGrid, "display", "grid"); | |||
sp(catGrid, "grid-template-columns", "repeat(5, 1fr)"); | |||
sp(catGrid, "gap", "8px"); | |||
sp(catGrid, "margin-bottom", "2.5rem"); | |||
var countEls = {}; | |||
CATS.forEach(function(cat) { | |||
var card = document.createElement("a"); | |||
card.href = cat.href; | |||
sp(card, "border", "1px solid #e2d9c8"); | |||
sp(card, "border-radius", "6px"); | |||
sp(card, "background", "#ffffff"); | |||
sp(card, "padding", "1rem 0.5rem"); | |||
sp(card, "text-align", "center"); | |||
sp(card, "text-decoration", "none"); | |||
sp(card, "display", "block"); | |||
sp(card, "box-sizing", "border-box"); | |||
card.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); this.style.setProperty("border-color", "#c8b99a", "important"); }); | |||
card.addEventListener("mouseout", function() { this.style.setProperty("background", "#ffffff", "important"); this.style.setProperty("border-color", "#e2d9c8", "important"); }); | |||
var icon = document.createElement("i"); | |||
icon.className = "ti " + cat.icon; | |||
sp(icon, "font-size", "1.25rem"); | |||
sp(icon, "color", "#c8a060"); | |||
sp(icon, "display", "block"); | |||
sp(icon, "margin-bottom", "0.4rem"); | |||
var name = document.createElement("div"); | |||
name.textContent = cat.label; | |||
sp(name, "font-size", "0.75rem"); | |||
sp(name, "font-weight", "500"); | |||
sp(name, "color", "#2a2218"); | |||
sp(name, "font-family", "'Inter', sans-serif"); | |||
var count = document.createElement("div"); | |||
count.textContent = "—"; | |||
sp(count, "font-size", "0.65rem"); | |||
sp(count, "color", "#9c8e80"); | |||
sp(count, "margin-top", "0.2rem"); | |||
sp(count, "font-family", "'Inter', sans-serif"); | |||
countEls[cat.label] = count; | |||
card.appendChild(icon); | |||
card.appendChild(name); | |||
card.appendChild(count); | |||
catGrid.appendChild(card); | |||
}); | |||
wrap.appendChild(catGrid); | |||
// 카테고리 문서 수 — 단일 API 요청 | |||
var catTitles = CATS.map(function(c) { return "Category:" + c.label; }).join("|"); | |||
fetch("/api.php?action=query&prop=categoryinfo&titles=" + encodeURIComponent(catTitles) + "&format=json") | |||
.then(function(r) { return r.json(); }) | |||
.then(function(data) { | |||
var pages = data.query && data.query.pages ? data.query.pages : {}; | |||
var totalDocs = 0; | |||
Object.keys(pages).forEach(function(id) { | |||
var page = pages[id]; | |||
var catName = (page.title || "").replace("Category:", ""); | |||
var cnt = (page.categoryinfo && page.categoryinfo.pages) ? page.categoryinfo.pages : 0; | |||
if (countEls[catName]) { | |||
countEls[catName].textContent = cnt + (cnt === 1 ? " article" : " articles"); | |||
} | |||
totalDocs += cnt; | |||
}); | |||
statNum.textContent = totalDocs; | |||
}) | |||
.catch(function() {}); | |||
// ── 하단 2컬럼 | |||
var twoCol = document.createElement("div"); | |||
sp(twoCol, "display", "grid"); | |||
sp(twoCol, "grid-template-columns", "1fr 1fr"); | |||
sp(twoCol, "gap", "1.5rem"); | |||
// New to Codex? | |||
var welcomeWrap = document.createElement("div"); | |||
welcomeWrap.appendChild(makeSectionTitle("New to Erakh Codex?")); | |||
var welcomeBox = document.createElement("div"); | |||
sp(welcomeBox, "background", "#ffffff"); | |||
sp(welcomeBox, "border", "1px solid #e2d9c8"); | |||
sp(welcomeBox, "border-radius", "6px"); | |||
sp(welcomeBox, "padding", "1.25rem 1.5rem"); | |||
sp(welcomeBox, "display", "flex"); | |||
sp(welcomeBox, "align-items", "flex-start"); | |||
sp(welcomeBox, "gap", "0.875rem"); | |||
sp(welcomeBox, "box-sizing", "border-box"); | |||
var wIcon = document.createElement("i"); | |||
wIcon.className = "ti ti-info-circle"; | |||
sp(wIcon, "font-size", "1.125rem"); | |||
sp(wIcon, "color", "#c8a060"); | |||
sp(wIcon, "flex-shrink", "0"); | |||
sp(wIcon, "margin-top", "0.1rem"); | |||
var wText = document.createElement("div"); | |||
var wTitle = document.createElement("div"); | |||
wTitle.textContent = "Welcome to Erakh Codex"; | |||
sp(wTitle, "font-size", "0.875rem"); | |||
sp(wTitle, "font-weight", "500"); | |||
sp(wTitle, "color", "#2a2218"); | |||
sp(wTitle, "margin-bottom", "0.3rem"); | |||
sp(wTitle, "font-family", "'Inter', sans-serif"); | |||
var wDesc = document.createElement("div"); | |||
sp(wDesc, "font-size", "0.775rem"); | |||
sp(wDesc, "color", "#6b5e4e"); | |||
sp(wDesc, "line-height", "1.65"); | |||
sp(wDesc, "font-family", "'Inter', sans-serif"); | |||
wDesc.innerHTML = "The official encyclopedia of the Mirtess Chronicle, maintained by the author. Before contributing, please read the <a href='/index.php?title=Erakh_Codex:Policies' style='color:#c8a060;text-decoration:none;font-weight:500;'>Policies & Guidelines</a>."; | |||
wText.appendChild(wTitle); | |||
wText.appendChild(wDesc); | |||
welcomeBox.appendChild(wIcon); | |||
welcomeBox.appendChild(wText); | |||
welcomeWrap.appendChild(welcomeBox); | |||
twoCol.appendChild(welcomeWrap); | |||
// Recent Changes | |||
var recentWrap = document.createElement("div"); | |||
recentWrap.appendChild(makeSectionTitle("Recent Changes")); | |||
var recentBox = document.createElement("div"); | |||
sp(recentBox, "background", "#ffffff"); | |||
sp(recentBox, "border", "1px solid #e2d9c8"); | |||
sp(recentBox, "border-radius", "6px"); | |||
sp(recentBox, "overflow", "hidden"); | |||
// 로딩 상태 | |||
var loadingMsg = document.createElement("div"); | |||
loadingMsg.textContent = "Loading..."; | |||
sp(loadingMsg, "padding", "1rem"); | |||
sp(loadingMsg, "font-size", "0.78rem"); | |||
sp(loadingMsg, "color", "#9c8e80"); | |||
sp(loadingMsg, "font-family", "'Inter', sans-serif"); | |||
recentBox.appendChild(loadingMsg); | |||
// Recent Changes API 호출 | |||
fetch("/api.php?action=query&list=recentchanges&rcnamespace=0&rclimit=10&rcprop=title|timestamp&rctype=edit|new&format=json") | |||
.then(function(r) { return r.json(); }) | |||
.then(function(data) { | |||
recentBox.innerHTML = ""; | |||
var changes = data.query && data.query.recentchanges ? data.query.recentchanges : []; | |||
// 제목 중복 제거 | |||
var seen = {}; | |||
var unique = []; | |||
changes.forEach(function(c) { | |||
if (!seen[c.title]) { seen[c.title] = true; unique.push(c); } | |||
}); | |||
if (unique.length === 0) { | |||
var empty = document.createElement("div"); | |||
empty.textContent = "No documents yet."; | |||
sp(empty, "padding", "1.5rem 1rem"); | |||
sp(empty, "font-size", "0.78rem"); | |||
sp(empty, "color", "#9c8e80"); | |||
sp(empty, "text-align", "center"); | |||
sp(empty, "font-family", "'Inter', sans-serif"); | |||
recentBox.appendChild(empty); | |||
return; | |||
} | |||
unique.forEach(function(c, i) { | |||
var row = document.createElement("div"); | |||
sp(row, "display", "flex"); | |||
sp(row, "align-items", "center"); | |||
sp(row, "justify-content", "space-between"); | |||
sp(row, "padding", "0.55rem 1rem"); | |||
sp(row, "border-bottom", i < unique.length - 1 ? "1px solid #f0ebe0" : "none"); | |||
var title = document.createElement("div"); | |||
title.textContent = c.title; | |||
sp(title, "font-size", "0.78rem"); | |||
sp(title, "color", "#2a2218"); | |||
sp(title, "font-family", "'Inter', sans-serif"); | |||
var date = document.createElement("div"); | |||
var d = new Date(c.timestamp); | |||
date.textContent = d.getFullYear() + "." + String(d.getMonth() + 1).padStart(2, "0") + "." + String(d.getDate()).padStart(2, "0"); | |||
sp(date, "font-size", "0.7rem"); | |||
sp(date, "color", "#9c8e80"); | |||
sp(date, "flex-shrink", "0"); | |||
sp(date, "margin-left", "1rem"); | |||
sp(date, "font-family", "'Inter', sans-serif"); | |||
row.appendChild(title); | |||
row.appendChild(date); | |||
recentBox.appendChild(row); | |||
}); | |||
}) | |||
.catch(function() { | |||
recentBox.innerHTML = ""; | |||
var err = document.createElement("div"); | |||
err.textContent = "Could not load recent changes."; | |||
sp(err, "padding", "1rem"); | |||
sp(err, "font-size", "0.78rem"); | |||
sp(err, "color", "#9c8e80"); | |||
sp(err, "font-family", "'Inter', sans-serif"); | |||
recentBox.appendChild(err); | |||
}); | |||
recentWrap.appendChild(recentBox); | |||
twoCol.appendChild(recentWrap); | |||
wrap.appendChild(twoCol); | |||
target.appendChild(wrap); | |||
} | } | ||
| Line 977: | Line 1,299: | ||
buildLayout(); | buildLayout(); | ||
buildCreatePage(); | buildCreatePage(); | ||
buildMainPage(); | |||
}); | }); | ||
} else { | } else { | ||
buildLayout(); | buildLayout(); | ||
buildCreatePage(); | buildCreatePage(); | ||
buildMainPage(); | |||
} | } | ||
})(); | })(); | ||
Revision as of 17:32, 10 May 2026
(function () {
var fontLink = document.createElement("link");
fontLink.rel = "stylesheet";
fontLink.href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap";
document.head.insertBefore(fontLink, document.head.firstChild);
function getCookie(name) {
var match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
return match ? decodeURIComponent(match[1]) : null;
}
var user = null;
try {
var raw = getCookie("erakh_user");
if (raw) user = JSON.parse(raw);
} catch (e) {}
var pageName = mw.config.get("wgPageName");
var isSysop = (mw.config.get("wgUserGroups") || []).indexOf("sysop") !== -1;
// proactive 토큰 갱신
if (user && user.exp) {
var now = Math.floor(Date.now() / 1000);
var remaining = user.exp - now;
if (remaining < 5 * 60) {
fetch("https://erakh.com/api/auth/refresh", { method: "POST", credentials: "include" })
.catch(function() {});
} else {
var delay = (remaining - 5 * 60) * 1000;
setTimeout(function() {
fetch("https://erakh.com/api/auth/refresh", { method: "POST", credentials: "include" })
.catch(function() {});
}, delay);
}
}
function sp(el, prop, val) {
el.style.setProperty(prop, val, "important");
}
// ─── 헤더 ───
var header = document.createElement("header");
sp(header, "position", "fixed");
sp(header, "top", "0");
sp(header, "left", "0");
sp(header, "right", "0");
sp(header, "z-index", "9999");
sp(header, "width", "100%");
sp(header, "background", "#f9f6f0");
sp(header, "border-bottom", "1px solid #e2d9c8");
sp(header, "display", "flex");
sp(header, "align-items", "center");
sp(header, "justify-content", "space-between");
sp(header, "padding", "0 2rem");
sp(header, "height", "56px");
sp(header, "box-sizing", "border-box");
var left = document.createElement("div");
sp(left, "display", "flex");
sp(left, "align-items", "center");
sp(left, "gap", "1.75rem");
var logo = document.createElement("a");
logo.href = "https://erakh.com";
logo.textContent = "Erakh Archive";
sp(logo, "font-family", "'Inter', sans-serif");
sp(logo, "font-size", "1.125rem");
sp(logo, "font-weight", "500");
sp(logo, "color", "#2a2218");
sp(logo, "text-decoration", "none");
sp(logo, "letter-spacing", "-0.01em");
sp(logo, "text-transform", "none");
sp(logo, "font-variant", "normal");
sp(logo, "font-variant-caps", "normal");
sp(logo, "line-height", "1");
var nav = document.createElement("nav");
sp(nav, "display", "flex");
sp(nav, "align-items", "center");
sp(nav, "gap", "1.5rem");
var codexLink = document.createElement("a");
codexLink.href = "https://codex.erakh.com";
codexLink.textContent = "Codex";
sp(codexLink, "font-family", "'Inter', sans-serif");
sp(codexLink, "font-size", "1rem");
sp(codexLink, "color", "#6b5e4e");
sp(codexLink, "text-decoration", "none");
sp(codexLink, "text-transform", "none");
sp(codexLink, "font-variant", "normal");
sp(codexLink, "line-height", "1");
codexLink.addEventListener("mouseover", function() { this.style.setProperty("color", "#2a2218", "important"); });
codexLink.addEventListener("mouseout", function() { this.style.setProperty("color", "#6b5e4e", "important"); });
nav.appendChild(codexLink);
left.appendChild(logo);
left.appendChild(nav);
var right = document.createElement("div");
sp(right, "display", "flex");
sp(right, "align-items", "center");
if (user && user.nickname) {
var profile = document.createElement("div");
sp(profile, "position", "relative");
var avatar = document.createElement("div");
avatar.textContent = user.nickname.slice(0, 1).toUpperCase();
sp(avatar, "width", "32px");
sp(avatar, "height", "32px");
sp(avatar, "border-radius", "50%");
sp(avatar, "background", "#e2d9c8");
sp(avatar, "border", "1px solid #c8b99a");
sp(avatar, "display", "flex");
sp(avatar, "align-items", "center");
sp(avatar, "justify-content", "center");
sp(avatar, "cursor", "pointer");
sp(avatar, "font-family", "'Inter', sans-serif");
sp(avatar, "font-size", "0.75rem");
sp(avatar, "font-weight", "500");
sp(avatar, "color", "#2a2218");
sp(avatar, "box-sizing", "border-box");
var dropdown = document.createElement("div");
sp(dropdown, "display", "none");
sp(dropdown, "position", "absolute");
sp(dropdown, "top", "calc(100% + 8px)");
sp(dropdown, "right", "0");
sp(dropdown, "background", "#f9f6f0");
sp(dropdown, "border", "1px solid #e2d9c8");
sp(dropdown, "min-width", "220px");
sp(dropdown, "padding", "0");
sp(dropdown, "box-shadow", "0 4px 16px rgba(0,0,0,0.06)");
sp(dropdown, "z-index", "10000");
sp(dropdown, "box-sizing", "border-box");
var dpProfile = document.createElement("div");
sp(dpProfile, "padding", "1rem");
sp(dpProfile, "display", "flex");
sp(dpProfile, "flex-direction", "column");
sp(dpProfile, "align-items", "center");
sp(dpProfile, "gap", "0.25rem");
sp(dpProfile, "border-bottom", "1px solid #e2d9c8");
var dpAvatar = document.createElement("div");
dpAvatar.textContent = user.nickname.slice(0, 1).toUpperCase();
sp(dpAvatar, "width", "44px");
sp(dpAvatar, "height", "44px");
sp(dpAvatar, "border-radius", "50%");
sp(dpAvatar, "background", "#f0ebe0");
sp(dpAvatar, "border", "1px solid #c8b99a");
sp(dpAvatar, "display", "flex");
sp(dpAvatar, "align-items", "center");
sp(dpAvatar, "justify-content", "center");
sp(dpAvatar, "font-family", "'Inter', sans-serif");
sp(dpAvatar, "font-size", "1rem");
sp(dpAvatar, "font-weight", "500");
sp(dpAvatar, "color", "#c8a060");
sp(dpAvatar, "margin-bottom", "0.25rem");
var dpName = document.createElement("div");
dpName.textContent = user.nickname;
sp(dpName, "font-family", "'Inter', sans-serif");
sp(dpName, "font-size", "0.875rem");
sp(dpName, "font-weight", "500");
sp(dpName, "color", "#2a2218");
var dpTitle = document.createElement("div");
dpTitle.textContent = user.title || "Chronicler";
sp(dpTitle, "font-family", "'Inter', sans-serif");
sp(dpTitle, "font-size", "0.6875rem");
sp(dpTitle, "color", "#9c8e80");
sp(dpTitle, "font-style", "italic");
dpProfile.appendChild(dpAvatar);
dpProfile.appendChild(dpName);
dpProfile.appendChild(dpTitle);
var dpStats = document.createElement("div");
sp(dpStats, "display", "grid");
sp(dpStats, "grid-template-columns", "1fr 1fr");
sp(dpStats, "border-bottom", "1px solid #e2d9c8");
function makeStatCell(val, lbl, borderRight, borderBottom) {
var cell = document.createElement("div");
sp(cell, "padding", "0.5rem 0");
sp(cell, "text-align", "center");
if (borderRight) sp(cell, "border-right", "1px solid #e2d9c8");
if (borderBottom) sp(cell, "border-bottom", "1px solid #e2d9c8");
var valEl = document.createElement("div");
valEl.textContent = val;
valEl.className = "dp-stat-val";
sp(valEl, "font-family", "'Inter', sans-serif");
sp(valEl, "font-size", "0.8125rem");
sp(valEl, "font-weight", "500");
sp(valEl, "color", "#2a2218");
sp(valEl, "margin-bottom", "2px");
var lblEl = document.createElement("div");
lblEl.textContent = lbl;
sp(lblEl, "font-family", "'Inter', sans-serif");
sp(lblEl, "font-size", "0.5625rem");
sp(lblEl, "letter-spacing", "0.1em");
sp(lblEl, "color", "#9c8e80");
sp(lblEl, "text-transform", "uppercase");
cell.appendChild(valEl);
cell.appendChild(lblEl);
return cell;
}
var cellLevel = makeStatCell("—", "Level", true, true);
var cellExp = makeStatCell("—", "EXP", false, true);
var cellDays = makeStatCell("—", "Days Active", true, false);
var cellEdits = makeStatCell("—", "Edits", false, false);
dpStats.appendChild(cellLevel);
dpStats.appendChild(cellExp);
dpStats.appendChild(cellDays);
dpStats.appendChild(cellEdits);
var dpMenu = document.createElement("div");
sp(dpMenu, "padding", "0.25rem 0");
var profileLink = document.createElement("a");
profileLink.href = "https://erakh.com/profile";
profileLink.textContent = "Profile";
sp(profileLink, "display", "block");
sp(profileLink, "width", "100%");
sp(profileLink, "padding", "0.5rem 1rem");
sp(profileLink, "font-family", "'Inter', sans-serif");
sp(profileLink, "font-size", "0.8125rem");
sp(profileLink, "color", "#2a2218");
sp(profileLink, "text-decoration", "none");
sp(profileLink, "box-sizing", "border-box");
profileLink.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); });
profileLink.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); });
var signOutBtn = document.createElement("button");
signOutBtn.textContent = "Sign out";
sp(signOutBtn, "display", "block");
sp(signOutBtn, "width", "100%");
sp(signOutBtn, "padding", "0.5rem 1rem");
sp(signOutBtn, "font-family", "'Inter', sans-serif");
sp(signOutBtn, "font-size", "0.8125rem");
sp(signOutBtn, "color", "#a04040");
sp(signOutBtn, "text-align", "left");
sp(signOutBtn, "background", "transparent");
sp(signOutBtn, "border", "none");
sp(signOutBtn, "cursor", "pointer");
sp(signOutBtn, "box-sizing", "border-box");
signOutBtn.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); });
signOutBtn.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); });
signOutBtn.addEventListener("click", function () {
fetch("https://erakh.com/api/auth/logout", { method: "POST", credentials: "include" })
.then(function () { window.location.href = "https://erakh.com"; });
});
dpMenu.appendChild(profileLink);
dpMenu.appendChild(signOutBtn);
dropdown.appendChild(dpProfile);
dropdown.appendChild(dpStats);
dropdown.appendChild(dpMenu);
var profileLoaded = false;
avatar.addEventListener("click", function () {
var isHidden = dropdown.style.display === "none";
dropdown.style.display = isHidden ? "block" : "none";
if (isHidden && !profileLoaded) {
fetch("https://erakh.com/api/auth/me", { method: "GET", credentials: "include" })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.isNew) return;
profileLoaded = true;
cellLevel.querySelector(".dp-stat-val").textContent = "Lv. " + data.level;
cellExp.querySelector(".dp-stat-val").textContent = data.exp_in_level + " / " + data.exp_to_next;
cellDays.querySelector(".dp-stat-val").textContent = data.login_days;
cellEdits.querySelector(".dp-stat-val").textContent = data.edit_count;
})
.catch(function() {});
}
});
document.addEventListener("click", function (e) {
if (!profile.contains(e.target)) {
dropdown.style.display = "none";
}
});
profile.appendChild(avatar);
profile.appendChild(dropdown);
right.appendChild(profile);
} else {
var loginBtn = document.createElement("a");
loginBtn.href = "https://erakh.com/login?redirect=" + encodeURIComponent(window.location.href);
loginBtn.textContent = "Sign in";
sp(loginBtn, "font-family", "'Inter', sans-serif");
sp(loginBtn, "font-size", "0.8125rem");
sp(loginBtn, "font-weight", "500");
sp(loginBtn, "color", "#2a2218");
sp(loginBtn, "text-decoration", "none");
sp(loginBtn, "padding", "0.4rem 1rem");
sp(loginBtn, "border", "1px solid #c8b99a");
sp(loginBtn, "background", "transparent");
sp(loginBtn, "cursor", "pointer");
sp(loginBtn, "box-sizing", "border-box");
sp(loginBtn, "line-height", "1");
loginBtn.addEventListener("mouseover", function() {
this.style.setProperty("border-color", "#c8a060", "important");
this.style.setProperty("background", "#f0ebe0", "important");
});
loginBtn.addEventListener("mouseout", function() {
this.style.setProperty("border-color", "#c8b99a", "important");
this.style.setProperty("background", "transparent", "important");
});
right.appendChild(loginBtn);
}
header.appendChild(left);
header.appendChild(right);
document.body.insertBefore(header, document.body.firstChild);
// ─── 서브바 ───
var subbar = document.createElement("div");
sp(subbar, "position", "fixed");
sp(subbar, "top", "56px");
sp(subbar, "left", "0");
sp(subbar, "right", "0");
sp(subbar, "z-index", "9998");
sp(subbar, "height", "44px");
sp(subbar, "background", "#f0ebe0");
sp(subbar, "border-bottom", "1px solid #e2d9c8");
sp(subbar, "display", "flex");
sp(subbar, "align-items", "center");
sp(subbar, "gap", "1rem");
sp(subbar, "padding", "0 1.5rem");
sp(subbar, "box-sizing", "border-box");
sp(subbar, "width", "100%");
var hbBtn = document.createElement("button");
sp(hbBtn, "display", "flex");
sp(hbBtn, "flex-direction", "column");
sp(hbBtn, "justify-content", "center");
sp(hbBtn, "align-items", "center");
sp(hbBtn, "gap", "5px");
sp(hbBtn, "width", "32px");
sp(hbBtn, "height", "32px");
sp(hbBtn, "border", "none");
sp(hbBtn, "background", "transparent");
sp(hbBtn, "cursor", "pointer");
sp(hbBtn, "padding", "4px");
sp(hbBtn, "border-radius", "4px");
sp(hbBtn, "flex-shrink", "0");
sp(hbBtn, "box-sizing", "border-box");
function makeBar() {
var b = document.createElement("span");
b.style.setProperty("display", "block", "important");
b.style.setProperty("width", "18px", "important");
b.style.setProperty("height", "1.5px", "important");
b.style.setProperty("background", "#6b5e4e", "important");
b.style.setProperty("border-radius", "1px", "important");
return b;
}
hbBtn.appendChild(makeBar());
hbBtn.appendChild(makeBar());
hbBtn.appendChild(makeBar());
hbBtn.addEventListener("mouseover", function() { this.style.setProperty("background", "#e2d9c8", "important"); });
hbBtn.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); });
var searchWrap = document.createElement("div");
sp(searchWrap, "position", "relative");
sp(searchWrap, "flex", "1");
sp(searchWrap, "max-width", "520px");
var searchIconEl = document.createElement("span");
searchIconEl.innerHTML = "⚲";
sp(searchIconEl, "position", "absolute");
sp(searchIconEl, "left", "0.6rem");
sp(searchIconEl, "top", "50%");
sp(searchIconEl, "transform", "translateY(-50%)");
sp(searchIconEl, "font-size", "13px");
sp(searchIconEl, "color", "#9c8e80");
sp(searchIconEl, "pointer-events", "none");
sp(searchIconEl, "line-height", "1");
var searchInput = document.createElement("input");
searchInput.type = "text";
searchInput.placeholder = "Search Erakh Codex...";
sp(searchInput, "width", "100%");
sp(searchInput, "height", "30px");
sp(searchInput, "padding", "0 0.75rem 0 2rem");
sp(searchInput, "border", "1px solid #c8b99a");
sp(searchInput, "border-radius", "3px");
sp(searchInput, "background", "#f9f6f0");
sp(searchInput, "font-family", "'Inter', sans-serif");
sp(searchInput, "font-size", "0.8125rem");
sp(searchInput, "color", "#2a2218");
sp(searchInput, "outline", "none");
sp(searchInput, "box-sizing", "border-box");
var suggestBox = document.createElement("div");
sp(suggestBox, "position", "absolute");
sp(suggestBox, "top", "calc(100% + 2px)");
sp(suggestBox, "left", "0");
sp(suggestBox, "right", "0");
sp(suggestBox, "background", "#f9f6f0");
sp(suggestBox, "border", "1px solid #c8b99a");
sp(suggestBox, "border-radius", "3px");
sp(suggestBox, "z-index", "10001");
sp(suggestBox, "display", "none");
sp(suggestBox, "box-shadow", "0 4px 12px rgba(0,0,0,0.08)");
searchWrap.appendChild(suggestBox);
var suggestItems = [];
var suggestIndex = -1;
function renderSuggestions(titles) {
suggestBox.innerHTML = "";
suggestItems = titles;
suggestIndex = -1;
if (titles.length === 0) { suggestBox.style.setProperty("display", "none", "important"); return; }
titles.forEach(function(title, i) {
var item = document.createElement("div");
item.textContent = title;
sp(item, "padding", "0.4rem 0.75rem");
sp(item, "font-family", "'Inter', sans-serif");
sp(item, "font-size", "0.8125rem");
sp(item, "color", "#2a2218");
sp(item, "cursor", "pointer");
sp(item, "border-bottom", i < titles.length - 1 ? "1px solid #f0ebe0" : "none");
item.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); });
item.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); });
item.addEventListener("mousedown", function(e) {
e.preventDefault();
window.location.href = "/index.php?title=" + encodeURIComponent(title.replace(/ /g, "_"));
});
suggestBox.appendChild(item);
});
suggestBox.style.setProperty("display", "block", "important");
}
var suggestTimer = null;
searchInput.addEventListener("input", function() {
var q = this.value.trim();
clearTimeout(suggestTimer);
if (q.length < 2) { suggestBox.style.setProperty("display", "none", "important"); return; }
suggestTimer = setTimeout(function() {
fetch("/api.php?action=opensearch&search=" + encodeURIComponent(q) + "&limit=8&namespace=0&format=json")
.then(function(r) { return r.json(); })
.then(function(data) { renderSuggestions(data[1] || []); })
.catch(function() {});
}, 200);
});
searchInput.addEventListener("focus", function() { this.style.setProperty("border-color", "#c8a060", "important"); });
searchInput.addEventListener("blur", function() {
this.style.setProperty("border-color", "#c8b99a", "important");
setTimeout(function() { suggestBox.style.setProperty("display", "none", "important"); }, 150);
});
searchInput.addEventListener("keydown", function(e) {
var items = suggestBox.querySelectorAll("div");
if (e.key === "ArrowDown") {
e.preventDefault();
suggestIndex = Math.min(suggestIndex + 1, items.length - 1);
items.forEach(function(el, i) { el.style.setProperty("background", i === suggestIndex ? "#f0ebe0" : "transparent", "important"); });
} else if (e.key === "ArrowUp") {
e.preventDefault();
suggestIndex = Math.max(suggestIndex - 1, -1);
items.forEach(function(el, i) { el.style.setProperty("background", i === suggestIndex ? "#f0ebe0" : "transparent", "important"); });
} else if (e.key === "Enter") {
e.preventDefault();
if (suggestIndex >= 0 && suggestItems[suggestIndex]) {
window.location.href = "/index.php?title=" + encodeURIComponent(suggestItems[suggestIndex].replace(/ /g, "_"));
} else if (this.value.trim()) {
window.location.href = "/index.php?title=Special%3ASearch&search=" + encodeURIComponent(this.value.trim());
}
} else if (e.key === "Escape") {
suggestBox.style.setProperty("display", "none", "important");
}
});
searchWrap.appendChild(searchIconEl);
searchWrap.appendChild(searchInput);
subbar.appendChild(hbBtn);
subbar.appendChild(searchWrap);
if (isSysop) {
var createDocBtn = document.createElement("a");
createDocBtn.href = "/index.php?title=Erakh_Codex:Create";
createDocBtn.textContent = "+ Create";
sp(createDocBtn, "display", "flex");
sp(createDocBtn, "align-items", "center");
sp(createDocBtn, "height", "44px");
sp(createDocBtn, "padding", "0 1.25rem");
sp(createDocBtn, "margin-left", "auto");
sp(createDocBtn, "font-family", "'Inter', sans-serif");
sp(createDocBtn, "font-size", "0.8125rem");
sp(createDocBtn, "font-weight", "500");
sp(createDocBtn, "color", "#c8a060");
sp(createDocBtn, "text-decoration", "none");
sp(createDocBtn, "border-left", "1px solid #e2d9c8");
sp(createDocBtn, "flex-shrink", "0");
sp(createDocBtn, "box-sizing", "border-box");
sp(createDocBtn, "letter-spacing", "-0.01em");
createDocBtn.addEventListener("mouseover", function() {
this.style.setProperty("background", "#e2d9c8", "important");
this.style.setProperty("color", "#a07840", "important");
});
createDocBtn.addEventListener("mouseout", function() {
this.style.setProperty("background", "transparent", "important");
this.style.setProperty("color", "#c8a060", "important");
});
subbar.appendChild(createDocBtn);
}
// ─── 드로어 ───
var drawerOverlay = document.createElement("div");
sp(drawerOverlay, "position", "fixed");
sp(drawerOverlay, "top", "100px");
sp(drawerOverlay, "left", "0");
sp(drawerOverlay, "right", "0");
sp(drawerOverlay, "bottom", "0");
sp(drawerOverlay, "background", "rgba(0,0,0,0.18)");
sp(drawerOverlay, "z-index", "9996");
sp(drawerOverlay, "display", "none");
var drawer = document.createElement("nav");
sp(drawer, "position", "fixed");
sp(drawer, "top", "100px");
sp(drawer, "left", "0");
sp(drawer, "bottom", "0");
sp(drawer, "width", "220px");
sp(drawer, "background", "#f9f6f0");
sp(drawer, "border-right", "1px solid #e2d9c8");
sp(drawer, "z-index", "9997");
sp(drawer, "display", "none");
sp(drawer, "overflow-y", "auto");
var drawerHead = document.createElement("div");
sp(drawerHead, "padding", "0.75rem 1rem 0.5rem");
sp(drawerHead, "border-bottom", "1px solid #e2d9c8");
var drawerLabel = document.createElement("span");
drawerLabel.textContent = "Menu";
sp(drawerLabel, "font-family", "'Inter', sans-serif");
sp(drawerLabel, "font-size", "0.78rem");
sp(drawerLabel, "font-weight", "500");
sp(drawerLabel, "color", "#2a2218");
sp(drawerLabel, "display", "inline-block");
sp(drawerLabel, "padding-bottom", "0.25rem");
sp(drawerLabel, "border-bottom", "2px solid #c8a060");
drawerHead.appendChild(drawerLabel);
drawer.appendChild(drawerHead);
var drawerMenus = [
{
items: [
{ label: "Main Page", href: "/" },
{ label: "Categories", href: "/index.php?title=Special:Categories" },
{ label: "Timeline", href: "/index.php?title=Category:Chronicle" },
{ label: "Recent Changes", href: "/index.php?title=Special:RecentChanges" },
{ label: "Random Article", href: "/index.php?title=Special:Random" },
],
gold: true
},
{
items: [
{ label: "Policies & Guidelines", href: "/index.php?title=Erakh_Codex:Policies" },
],
gold: false
}
];
drawerMenus.forEach(function(section, si) {
var sec = document.createElement("div");
sp(sec, "padding", "0.5rem 0");
if (si < drawerMenus.length - 1) sp(sec, "border-bottom", "1px solid #e2d9c8");
section.items.forEach(function(item) {
var a = document.createElement("a");
a.href = item.href;
a.textContent = item.label;
sp(a, "display", "block");
sp(a, "padding", "0.45rem 1.25rem");
sp(a, "font-family", "'Inter', sans-serif");
sp(a, "font-size", "0.8125rem");
sp(a, "color", section.gold ? "#c8a060" : "#6b5e4e");
sp(a, "text-decoration", "none");
a.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); });
a.addEventListener("mouseout", function() { this.style.setProperty("background", "transparent", "important"); });
sec.appendChild(a);
});
drawer.appendChild(sec);
});
var drawerOpen = false;
function toggleDrawer() {
drawerOpen = !drawerOpen;
drawer.style.setProperty("display", drawerOpen ? "block" : "none", "important");
drawerOverlay.style.setProperty("display", drawerOpen ? "block" : "none", "important");
}
hbBtn.addEventListener("click", toggleDrawer);
drawerOverlay.addEventListener("click", toggleDrawer);
document.body.insertBefore(subbar, document.body.children[1]);
document.body.appendChild(drawerOverlay);
document.body.appendChild(drawer);
// ─── 3컬럼 레이아웃 (JS 직접 목차 빌드) ───
function buildToc(headings) {
var toc = document.createElement("nav");
toc.id = "erakh-toc";
var ul = document.createElement("ul");
sp(ul, "list-style", "none");
sp(ul, "margin", "0");
sp(ul, "padding", "0");
headings.forEach(function(h, i) {
var level = parseInt(h.tagName.replace("H", ""));
var id = h.id || ("erakh-section-" + i);
h.id = id;
var li = document.createElement("li");
li.style.setProperty("padding", "0.25rem 0 0.25rem " + (level === 2 ? "0.5rem" : "1.25rem"), "important");
li.style.setProperty("border-left", "2px solid transparent", "important");
li.style.setProperty("line-height", "1.4", "important");
li.dataset.id = id;
var a = document.createElement("a");
a.href = "#" + id;
a.textContent = h.textContent.replace(/\[edit\]/g, "").trim();
sp(a, "font-family", "'Inter', sans-serif");
sp(a, "font-size", level === 2 ? "0.78rem" : "0.74rem");
sp(a, "color", level === 2 ? "#6b5e4e" : "#9c8e80");
sp(a, "text-decoration", "none");
sp(a, "display", "block");
a.addEventListener("mouseover", function() { this.style.setProperty("color", "#2a2218", "important"); });
a.addEventListener("mouseout", function() { this.style.setProperty("color", level === 2 ? "#6b5e4e" : "#9c8e80", "important"); });
li.appendChild(a);
ul.appendChild(li);
});
toc.appendChild(ul);
return toc;
}
function buildLayout() {
// 메인페이지 / Create 페이지에서는 레이아웃 빌드 스킵
if (pageName === "Erakh_Codex:Create") return;
if (pageName === "Main_Page") return;
var content = document.querySelector(".mw-body") || document.getElementById("content");
if (!content) return;
var headings = Array.from(content.querySelectorAll("h2, h3")).filter(function(h) {
return !h.closest("#toc");
});
var layout = document.createElement("div");
layout.className = "erakh-layout";
var colLeft = document.createElement("aside");
colLeft.className = "erakh-col-left";
var colCenter = document.createElement("div");
colCenter.className = "erakh-col-center";
var colRight = document.createElement("aside");
colRight.className = "erakh-col-right";
var tocLabel = document.createElement("div");
tocLabel.className = "erakh-toc-label";
tocLabel.textContent = "Contents";
colLeft.appendChild(tocLabel);
if (headings.length > 0) {
var toc = buildToc(headings);
colLeft.appendChild(toc);
var items = colLeft.querySelectorAll("li[data-id]");
window.addEventListener("scroll", function() {
var scrollY = window.scrollY + 108;
var current = null;
headings.forEach(function(h) {
if (h.offsetTop <= scrollY) current = h.id;
});
items.forEach(function(li) {
if (li.dataset.id === current) {
li.style.setProperty("border-left-color", "#c8a060", "important");
li.querySelector("a").style.setProperty("color", "#2a2218", "important");
li.querySelector("a").style.setProperty("font-weight", "500", "important");
} else {
li.style.setProperty("border-left-color", "transparent", "important");
var level = li.style.paddingLeft === "0.5rem" ? 2 : 3;
li.querySelector("a").style.setProperty("color", level === 2 ? "#6b5e4e" : "#9c8e80", "important");
li.querySelector("a").style.setProperty("font-weight", "400", "important");
}
});
});
} else {
var emptyMsg = document.createElement("div");
sp(emptyMsg, "font-family", "'Inter', sans-serif");
sp(emptyMsg, "font-size", "0.75rem");
sp(emptyMsg, "color", "#9c8e80");
sp(emptyMsg, "padding", "0.25rem 0.5rem");
emptyMsg.textContent = "—";
colLeft.appendChild(emptyMsg);
}
var sidebarLabel = document.createElement("div");
sidebarLabel.className = "erakh-sidebar-label";
sidebarLabel.textContent = "위젯 영역";
colRight.appendChild(sidebarLabel);
var ph1 = document.createElement("div");
ph1.className = "erakh-sidebar-placeholder";
ph1.textContent = "인기 문서 위젯 예정";
colRight.appendChild(ph1);
var ph2 = document.createElement("div");
ph2.className = "erakh-sidebar-placeholder";
ph2.textContent = "최근 변경 위젯 예정";
colRight.appendChild(ph2);
content.parentNode.insertBefore(layout, content);
colCenter.appendChild(content);
layout.appendChild(colLeft);
layout.appendChild(colCenter);
layout.appendChild(colRight);
}
// ─── 문서 생성 페이지 UI ───
function buildCreatePage() {
if (pageName !== "Erakh_Codex:Create") return;
var CATEGORIES = [
{ label: "Characters", preload: "Template:Preload/Character" },
{ label: "Nations", preload: "Template:Preload/Nation" },
{ label: "Regions", preload: "Template:Preload/Region" },
{ label: "Races", preload: "Template:Preload/Race" },
{ label: "Organizations", preload: "Template:Preload/Organization" },
{ label: "Events", preload: "Template:Preload/Event" },
{ label: "Disciplines", preload: "Template:Preload/Discipline" },
{ label: "Culture", preload: "Template:Preload/Culture" },
{ label: "Artifacts", preload: "Template:Preload/Artifact" },
{ label: "Chronicle", preload: null },
];
var firstHeading = document.getElementById("firstHeading");
if (firstHeading) sp(firstHeading, "display", "none");
var parseOutput = document.querySelector(".mw-parser-output");
if (parseOutput) sp(parseOutput, "display", "none");
var noArticle = document.querySelector(".noarticletext");
if (noArticle) sp(noArticle, "display", "none");
var pViews = document.getElementById("p-views");
if (pViews) sp(pViews, "display", "none");
var pCactions = document.getElementById("p-cactions");
if (pCactions) sp(pCactions, "display", "none");
var target = document.getElementById("mw-content-text");
if (!target) return;
if (!isSysop) {
var denied = document.createElement("div");
sp(denied, "padding", "60px 40px");
sp(denied, "text-align", "center");
sp(denied, "font-family", "'Inter', sans-serif");
sp(denied, "font-size", "0.875rem");
sp(denied, "color", "#9c8e80");
denied.textContent = "Document creation is restricted to administrators.";
target.appendChild(denied);
return;
}
var selectedCat = null;
var wrap = document.createElement("div");
sp(wrap, "max-width", "680px");
sp(wrap, "margin", "40px auto");
sp(wrap, "padding", "0 24px 48px");
sp(wrap, "font-family", "'Inter', sans-serif");
sp(wrap, "color", "#2a2218");
sp(wrap, "box-sizing", "border-box");
var heading = document.createElement("h2");
heading.textContent = "Create New Document";
sp(heading, "font-size", "1.375rem");
sp(heading, "font-weight", "600");
sp(heading, "margin", "0 0 2rem 0");
sp(heading, "color", "#2a2218");
sp(heading, "letter-spacing", "-0.01em");
sp(heading, "font-family", "'Inter', sans-serif");
wrap.appendChild(heading);
var catLabel = document.createElement("p");
catLabel.textContent = "Select Category";
sp(catLabel, "font-size", "0.6875rem");
sp(catLabel, "font-weight", "500");
sp(catLabel, "color", "#9c8e80");
sp(catLabel, "margin", "0 0 0.625rem 0");
sp(catLabel, "letter-spacing", "0.1em");
sp(catLabel, "text-transform", "uppercase");
sp(catLabel, "font-family", "'Inter', sans-serif");
wrap.appendChild(catLabel);
var grid = document.createElement("div");
sp(grid, "display", "grid");
sp(grid, "grid-template-columns", "repeat(5, 1fr)");
sp(grid, "gap", "8px");
sp(grid, "margin-bottom", "0.625rem");
var catCards = [];
CATEGORIES.forEach(function(cat) {
var card = document.createElement("button");
card.textContent = cat.label;
sp(card, "border", "1.5px solid #e2d9c8");
sp(card, "border-radius", "6px");
sp(card, "background", "#f9f6f0");
sp(card, "padding", "0.75rem 0.375rem");
sp(card, "font-size", "0.79rem");
sp(card, "font-weight", "500");
sp(card, "font-family", "'Inter', sans-serif");
sp(card, "color", "#2a2218");
sp(card, "cursor", "pointer");
sp(card, "text-align", "center");
sp(card, "line-height", "1.3");
sp(card, "box-sizing", "border-box");
card.addEventListener("mouseover", function() {
if (selectedCat !== cat.label) {
this.style.setProperty("background", "#f0ebe0", "important");
this.style.setProperty("border-color", "#c8b99a", "important");
}
});
card.addEventListener("mouseout", function() {
if (selectedCat !== cat.label) {
this.style.setProperty("background", "#f9f6f0", "important");
this.style.setProperty("border-color", "#e2d9c8", "important");
}
});
card.addEventListener("click", function() {
catCards.forEach(function(c) {
c.style.setProperty("background", "#f9f6f0", "important");
c.style.setProperty("border-color", "#e2d9c8", "important");
c.style.setProperty("color", "#2a2218", "important");
});
this.style.setProperty("background", "#c8a060", "important");
this.style.setProperty("border-color", "#c8a060", "important");
this.style.setProperty("color", "#ffffff", "important");
selectedCat = cat.label;
if (cat.preload === null) {
chronicleNotice.style.setProperty("display", "block", "important");
} else {
chronicleNotice.style.setProperty("display", "none", "important");
}
updateBtn();
});
catCards.push(card);
grid.appendChild(card);
});
wrap.appendChild(grid);
var chronicleNotice = document.createElement("p");
chronicleNotice.textContent = "Chronicle documents use a timeline format. No infobox template will be applied.";
sp(chronicleNotice, "display", "none");
sp(chronicleNotice, "font-size", "0.75rem");
sp(chronicleNotice, "color", "#9c8e80");
sp(chronicleNotice, "font-style", "italic");
sp(chronicleNotice, "margin", "0.375rem 0 0 0");
sp(chronicleNotice, "font-family", "'Inter', sans-serif");
wrap.appendChild(chronicleNotice);
var divider = document.createElement("div");
sp(divider, "border-top", "1px solid #e2d9c8");
sp(divider, "margin", "1.5rem 0 1.25rem");
wrap.appendChild(divider);
var titleLabel = document.createElement("p");
titleLabel.textContent = "Document Title";
sp(titleLabel, "font-size", "0.6875rem");
sp(titleLabel, "font-weight", "500");
sp(titleLabel, "color", "#9c8e80");
sp(titleLabel, "margin", "0 0 0.5rem 0");
sp(titleLabel, "letter-spacing", "0.1em");
sp(titleLabel, "text-transform", "uppercase");
sp(titleLabel, "font-family", "'Inter', sans-serif");
wrap.appendChild(titleLabel);
var titleInput = document.createElement("input");
titleInput.type = "text";
titleInput.placeholder = "Enter document title...";
sp(titleInput, "width", "100%");
sp(titleInput, "padding", "0.625rem 0.875rem");
sp(titleInput, "border", "1.5px solid #e2d9c8");
sp(titleInput, "border-radius", "6px");
sp(titleInput, "font-size", "0.9375rem");
sp(titleInput, "font-family", "'Inter', sans-serif");
sp(titleInput, "color", "#2a2218");
sp(titleInput, "background", "#ffffff");
sp(titleInput, "box-sizing", "border-box");
sp(titleInput, "outline", "none");
sp(titleInput, "margin-bottom", "1.25rem");
titleInput.addEventListener("focus", function() { this.style.setProperty("border-color", "#c8a060", "important"); });
titleInput.addEventListener("blur", function() { this.style.setProperty("border-color", "#e2d9c8", "important"); });
titleInput.addEventListener("input", updateBtn);
wrap.appendChild(titleInput);
var createBtn = document.createElement("button");
createBtn.textContent = "Create Article";
sp(createBtn, "display", "block");
sp(createBtn, "width", "100%");
sp(createBtn, "padding", "0.75rem");
sp(createBtn, "background", "#e2d9c8");
sp(createBtn, "border", "none");
sp(createBtn, "border-radius", "6px");
sp(createBtn, "font-size", "0.9375rem");
sp(createBtn, "font-weight", "600");
sp(createBtn, "font-family", "'Inter', sans-serif");
sp(createBtn, "color", "#9c8e80");
sp(createBtn, "cursor", "not-allowed");
sp(createBtn, "box-sizing", "border-box");
createBtn.addEventListener("click", function() {
if (!selectedCat) return;
var title = titleInput.value.trim();
if (!title) return;
var catObj = null;
for (var i = 0; i < CATEGORIES.length; i++) {
if (CATEGORIES[i].label === selectedCat) { catObj = CATEGORIES[i]; break; }
}
if (!catObj) return;
var url = "/index.php?title=" + encodeURIComponent(title) + "&action=edit";
if (catObj.preload) url += "&preload=" + encodeURIComponent(catObj.preload);
window.location.href = url;
});
wrap.appendChild(createBtn);
target.appendChild(wrap);
function updateBtn() {
var ready = selectedCat && titleInput.value.trim().length > 0;
if (ready) {
createBtn.style.setProperty("background", "#c8a060", "important");
createBtn.style.setProperty("color", "#ffffff", "important");
createBtn.style.setProperty("cursor", "pointer", "important");
} else {
createBtn.style.setProperty("background", "#e2d9c8", "important");
createBtn.style.setProperty("color", "#9c8e80", "important");
createBtn.style.setProperty("cursor", "not-allowed", "important");
}
}
}
// ─── 메인페이지 빌드 ───
function buildMainPage() {
if (pageName !== "Main_Page") return;
// MW 기본 콘텐츠 숨김
var firstHeading = document.getElementById("firstHeading");
if (firstHeading) sp(firstHeading, "display", "none");
var parseOutput = document.querySelector(".mw-parser-output");
if (parseOutput) sp(parseOutput, "display", "none");
var target = document.getElementById("mw-content-text");
if (!target) return;
var CATS = [
{ label: "Chronicle", icon: "ti-clock", href: "/index.php?title=Category:Chronicle" },
{ label: "Events", icon: "ti-sword", href: "/index.php?title=Category:Events" },
{ label: "Nations", icon: "ti-building", href: "/index.php?title=Category:Nations" },
{ label: "Regions", icon: "ti-map", href: "/index.php?title=Category:Regions" },
{ label: "Characters", icon: "ti-user", href: "/index.php?title=Category:Characters" },
{ label: "Races", icon: "ti-dna", href: "/index.php?title=Category:Races" },
{ label: "Organizations", icon: "ti-users", href: "/index.php?title=Category:Organizations" },
{ label: "Disciplines", icon: "ti-book", href: "/index.php?title=Category:Disciplines" },
{ label: "Culture", icon: "ti-music", href: "/index.php?title=Category:Culture" },
{ label: "Artifacts", icon: "ti-diamond", href: "/index.php?title=Category:Artifacts" },
];
// Tabler Icons 로드
var tablerLink = document.createElement("link");
tablerLink.rel = "stylesheet";
tablerLink.href = "https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css";
document.head.appendChild(tablerLink);
// ── 래퍼
var wrap = document.createElement("div");
sp(wrap, "max-width", "860px");
sp(wrap, "margin", "0 auto");
sp(wrap, "padding", "2.5rem 2rem 4rem");
sp(wrap, "font-family", "'Inter', sans-serif");
sp(wrap, "box-sizing", "border-box");
// ── Hero
var hero = document.createElement("div");
sp(hero, "background", "#f0ebe0");
sp(hero, "border", "1px solid #e2d9c8");
sp(hero, "border-radius", "6px");
sp(hero, "padding", "2rem 2.5rem");
sp(hero, "margin-bottom", "2.5rem");
sp(hero, "display", "flex");
sp(hero, "align-items", "center");
sp(hero, "justify-content", "space-between");
sp(hero, "box-sizing", "border-box");
var heroText = document.createElement("div");
var heroTitle = document.createElement("h1");
heroTitle.textContent = "Erakh Codex";
sp(heroTitle, "font-size", "1.5rem");
sp(heroTitle, "font-weight", "600");
sp(heroTitle, "color", "#2a2218");
sp(heroTitle, "letter-spacing", "-0.01em");
sp(heroTitle, "margin", "0 0 0.5rem 0");
sp(heroTitle, "font-family", "'Inter', sans-serif");
var heroDesc = document.createElement("p");
heroDesc.textContent = "The official encyclopedia of the Mirtess Chronicle. Explore the world, its history, peoples, and events — all in one place.";
sp(heroDesc, "font-size", "0.8125rem");
sp(heroDesc, "color", "#6b5e4e");
sp(heroDesc, "line-height", "1.7");
sp(heroDesc, "margin", "0");
sp(heroDesc, "max-width", "480px");
sp(heroDesc, "font-family", "'Inter', sans-serif");
heroText.appendChild(heroTitle);
heroText.appendChild(heroDesc);
var heroStat = document.createElement("div");
sp(heroStat, "text-align", "center");
sp(heroStat, "border-left", "1px solid #c8b99a");
sp(heroStat, "padding-left", "2.5rem");
sp(heroStat, "flex-shrink", "0");
var statNum = document.createElement("div");
statNum.textContent = "—";
sp(statNum, "font-size", "2rem");
sp(statNum, "font-weight", "600");
sp(statNum, "color", "#c8a060");
sp(statNum, "letter-spacing", "-0.02em");
sp(statNum, "font-family", "'Inter', sans-serif");
var statLbl = document.createElement("div");
statLbl.textContent = "Documents";
sp(statLbl, "font-size", "0.6875rem");
sp(statLbl, "color", "#9c8e80");
sp(statLbl, "text-transform", "uppercase");
sp(statLbl, "letter-spacing", "0.08em");
sp(statLbl, "margin-top", "0.25rem");
sp(statLbl, "font-family", "'Inter', sans-serif");
heroStat.appendChild(statNum);
heroStat.appendChild(statLbl);
hero.appendChild(heroText);
hero.appendChild(heroStat);
wrap.appendChild(hero);
// ── 섹션 타이틀 헬퍼
function makeSectionTitle(text) {
var t = document.createElement("div");
t.textContent = text;
sp(t, "font-size", "0.6875rem");
sp(t, "font-weight", "500");
sp(t, "color", "#9c8e80");
sp(t, "text-transform", "uppercase");
sp(t, "letter-spacing", "0.1em");
sp(t, "margin-bottom", "1rem");
sp(t, "padding-bottom", "0.4rem");
sp(t, "border-bottom", "1px solid #e2d9c8");
sp(t, "font-family", "'Inter', sans-serif");
return t;
}
// ── 카테고리 그리드
wrap.appendChild(makeSectionTitle("Browse by Category"));
var catGrid = document.createElement("div");
sp(catGrid, "display", "grid");
sp(catGrid, "grid-template-columns", "repeat(5, 1fr)");
sp(catGrid, "gap", "8px");
sp(catGrid, "margin-bottom", "2.5rem");
var countEls = {};
CATS.forEach(function(cat) {
var card = document.createElement("a");
card.href = cat.href;
sp(card, "border", "1px solid #e2d9c8");
sp(card, "border-radius", "6px");
sp(card, "background", "#ffffff");
sp(card, "padding", "1rem 0.5rem");
sp(card, "text-align", "center");
sp(card, "text-decoration", "none");
sp(card, "display", "block");
sp(card, "box-sizing", "border-box");
card.addEventListener("mouseover", function() { this.style.setProperty("background", "#f0ebe0", "important"); this.style.setProperty("border-color", "#c8b99a", "important"); });
card.addEventListener("mouseout", function() { this.style.setProperty("background", "#ffffff", "important"); this.style.setProperty("border-color", "#e2d9c8", "important"); });
var icon = document.createElement("i");
icon.className = "ti " + cat.icon;
sp(icon, "font-size", "1.25rem");
sp(icon, "color", "#c8a060");
sp(icon, "display", "block");
sp(icon, "margin-bottom", "0.4rem");
var name = document.createElement("div");
name.textContent = cat.label;
sp(name, "font-size", "0.75rem");
sp(name, "font-weight", "500");
sp(name, "color", "#2a2218");
sp(name, "font-family", "'Inter', sans-serif");
var count = document.createElement("div");
count.textContent = "—";
sp(count, "font-size", "0.65rem");
sp(count, "color", "#9c8e80");
sp(count, "margin-top", "0.2rem");
sp(count, "font-family", "'Inter', sans-serif");
countEls[cat.label] = count;
card.appendChild(icon);
card.appendChild(name);
card.appendChild(count);
catGrid.appendChild(card);
});
wrap.appendChild(catGrid);
// 카테고리 문서 수 — 단일 API 요청
var catTitles = CATS.map(function(c) { return "Category:" + c.label; }).join("|");
fetch("/api.php?action=query&prop=categoryinfo&titles=" + encodeURIComponent(catTitles) + "&format=json")
.then(function(r) { return r.json(); })
.then(function(data) {
var pages = data.query && data.query.pages ? data.query.pages : {};
var totalDocs = 0;
Object.keys(pages).forEach(function(id) {
var page = pages[id];
var catName = (page.title || "").replace("Category:", "");
var cnt = (page.categoryinfo && page.categoryinfo.pages) ? page.categoryinfo.pages : 0;
if (countEls[catName]) {
countEls[catName].textContent = cnt + (cnt === 1 ? " article" : " articles");
}
totalDocs += cnt;
});
statNum.textContent = totalDocs;
})
.catch(function() {});
// ── 하단 2컬럼
var twoCol = document.createElement("div");
sp(twoCol, "display", "grid");
sp(twoCol, "grid-template-columns", "1fr 1fr");
sp(twoCol, "gap", "1.5rem");
// New to Codex?
var welcomeWrap = document.createElement("div");
welcomeWrap.appendChild(makeSectionTitle("New to Erakh Codex?"));
var welcomeBox = document.createElement("div");
sp(welcomeBox, "background", "#ffffff");
sp(welcomeBox, "border", "1px solid #e2d9c8");
sp(welcomeBox, "border-radius", "6px");
sp(welcomeBox, "padding", "1.25rem 1.5rem");
sp(welcomeBox, "display", "flex");
sp(welcomeBox, "align-items", "flex-start");
sp(welcomeBox, "gap", "0.875rem");
sp(welcomeBox, "box-sizing", "border-box");
var wIcon = document.createElement("i");
wIcon.className = "ti ti-info-circle";
sp(wIcon, "font-size", "1.125rem");
sp(wIcon, "color", "#c8a060");
sp(wIcon, "flex-shrink", "0");
sp(wIcon, "margin-top", "0.1rem");
var wText = document.createElement("div");
var wTitle = document.createElement("div");
wTitle.textContent = "Welcome to Erakh Codex";
sp(wTitle, "font-size", "0.875rem");
sp(wTitle, "font-weight", "500");
sp(wTitle, "color", "#2a2218");
sp(wTitle, "margin-bottom", "0.3rem");
sp(wTitle, "font-family", "'Inter', sans-serif");
var wDesc = document.createElement("div");
sp(wDesc, "font-size", "0.775rem");
sp(wDesc, "color", "#6b5e4e");
sp(wDesc, "line-height", "1.65");
sp(wDesc, "font-family", "'Inter', sans-serif");
wDesc.innerHTML = "The official encyclopedia of the Mirtess Chronicle, maintained by the author. Before contributing, please read the <a href='/index.php?title=Erakh_Codex:Policies' style='color:#c8a060;text-decoration:none;font-weight:500;'>Policies & Guidelines</a>.";
wText.appendChild(wTitle);
wText.appendChild(wDesc);
welcomeBox.appendChild(wIcon);
welcomeBox.appendChild(wText);
welcomeWrap.appendChild(welcomeBox);
twoCol.appendChild(welcomeWrap);
// Recent Changes
var recentWrap = document.createElement("div");
recentWrap.appendChild(makeSectionTitle("Recent Changes"));
var recentBox = document.createElement("div");
sp(recentBox, "background", "#ffffff");
sp(recentBox, "border", "1px solid #e2d9c8");
sp(recentBox, "border-radius", "6px");
sp(recentBox, "overflow", "hidden");
// 로딩 상태
var loadingMsg = document.createElement("div");
loadingMsg.textContent = "Loading...";
sp(loadingMsg, "padding", "1rem");
sp(loadingMsg, "font-size", "0.78rem");
sp(loadingMsg, "color", "#9c8e80");
sp(loadingMsg, "font-family", "'Inter', sans-serif");
recentBox.appendChild(loadingMsg);
// Recent Changes API 호출
fetch("/api.php?action=query&list=recentchanges&rcnamespace=0&rclimit=10&rcprop=title|timestamp&rctype=edit|new&format=json")
.then(function(r) { return r.json(); })
.then(function(data) {
recentBox.innerHTML = "";
var changes = data.query && data.query.recentchanges ? data.query.recentchanges : [];
// 제목 중복 제거
var seen = {};
var unique = [];
changes.forEach(function(c) {
if (!seen[c.title]) { seen[c.title] = true; unique.push(c); }
});
if (unique.length === 0) {
var empty = document.createElement("div");
empty.textContent = "No documents yet.";
sp(empty, "padding", "1.5rem 1rem");
sp(empty, "font-size", "0.78rem");
sp(empty, "color", "#9c8e80");
sp(empty, "text-align", "center");
sp(empty, "font-family", "'Inter', sans-serif");
recentBox.appendChild(empty);
return;
}
unique.forEach(function(c, i) {
var row = document.createElement("div");
sp(row, "display", "flex");
sp(row, "align-items", "center");
sp(row, "justify-content", "space-between");
sp(row, "padding", "0.55rem 1rem");
sp(row, "border-bottom", i < unique.length - 1 ? "1px solid #f0ebe0" : "none");
var title = document.createElement("div");
title.textContent = c.title;
sp(title, "font-size", "0.78rem");
sp(title, "color", "#2a2218");
sp(title, "font-family", "'Inter', sans-serif");
var date = document.createElement("div");
var d = new Date(c.timestamp);
date.textContent = d.getFullYear() + "." + String(d.getMonth() + 1).padStart(2, "0") + "." + String(d.getDate()).padStart(2, "0");
sp(date, "font-size", "0.7rem");
sp(date, "color", "#9c8e80");
sp(date, "flex-shrink", "0");
sp(date, "margin-left", "1rem");
sp(date, "font-family", "'Inter', sans-serif");
row.appendChild(title);
row.appendChild(date);
recentBox.appendChild(row);
});
})
.catch(function() {
recentBox.innerHTML = "";
var err = document.createElement("div");
err.textContent = "Could not load recent changes.";
sp(err, "padding", "1rem");
sp(err, "font-size", "0.78rem");
sp(err, "color", "#9c8e80");
sp(err, "font-family", "'Inter', sans-serif");
recentBox.appendChild(err);
});
recentWrap.appendChild(recentBox);
twoCol.appendChild(recentWrap);
wrap.appendChild(twoCol);
target.appendChild(wrap);
}
// ─── 실행 ───
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function() {
buildLayout();
buildCreatePage();
buildMainPage();
});
} else {
buildLayout();
buildCreatePage();
buildMainPage();
}
})();