MediaWiki:Common.js

From Erakh Codex
Revision as of 21:12, 3 May 2026 by Juoh-6738 (talk | contribs)
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
(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) {}

  // 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 = "&#9906;";
  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);

  // ─── 드로어 ───
  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: "Recent Changes",  href: "/index.php?title=Special:RecentChanges" },
        { label: "Random Article",  href: "/index.php?title=Special:Random" },
      ],
      gold: true
    },
    {
      items: [
        { label: "Editing Guide",          href: "/index.php?title=Erakh_Codex:Editing_guide" },
        { label: "Policies & Guidelines",  href: "/index.php?title=Erakh_Codex:Policies" },
        { label: "Help",                   href: "/index.php?title=Help:Contents" },
      ],
      gold: false
    },
    {
      items: [
        { label: "About Codex",    href: "/index.php?title=Erakh_Codex:About" },
        { label: "Admin Requests", href: "/index.php?title=Erakh_Codex:Admin_requests" },
      ],
      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() {
    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);

      // 스크롤 하이라이트 (100px 고정 헤더 기준)
      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);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', buildLayout);
  } else {
    buildLayout();
  }

})();