// NEKO — Site core: constants, theme, shared tweaks, toast, navbar, footer // Loads after React/Babel + tweaks-panel.jsx. Exports to window. // One-time localStorage migration for the Troya → Neko rename. Any value still // stored under an old `troya-*` key is copied to its new `neko-*` key (without // clobbering newer data) and the stale key is removed. Idempotent — no-ops once // migrated, so it is safe to run on every load. Keeps saved cart/tweaks/theme/ // joined-events intact across the rename; Midnight-only behaviour is unaffected // because the theme value is still normalised below. (function nekoMigrateStorage() { try { var map = { "troya-theme": "neko-theme", "troya-tweaks": "neko-tweaks", "troya-cart-v1": "neko-cart-v1", "troya-ev-joined": "neko-ev-joined", }; for (var oldKey in map) { var oldVal = localStorage.getItem(oldKey); if (oldVal === null) continue; if (localStorage.getItem(map[oldKey]) === null) localStorage.setItem(map[oldKey], oldVal); localStorage.removeItem(oldKey); } } catch (e) {} })(); const NEKO = { ip: "mc.nekocraft.net", version: "1.13 – 26.1.2", discord: "discord.gg/Nm4uYfpaPn", online: 184, }; // --------------------------------------------------------------- // Theme — Midnight is the ONLY active site theme for now. The slider sun/moon // toggle is retired from public chrome; Sunlight lives only in the design // system as a passive/future reference. Lock forces Midnight and ignores any // previously-saved "sunlight" preference, so a refresh never resurfaces it. // --------------------------------------------------------------- const NEKO_THEME_LOCK = "midnight"; // null = switchable; "midnight" = locked function useNekoTheme() { const [theme, setThemeRaw] = React.useState(() => { if (NEKO_THEME_LOCK) return NEKO_THEME_LOCK; // ignore any saved preference for now try { return localStorage.getItem("neko-theme") || "midnight"; } catch (e) { return "midnight"; } }); React.useEffect(() => { document.body.classList.remove("theme-sunlight", "theme-midnight"); document.body.classList.add("theme-" + theme); // When locked, normalise any stale stored value back to the locked theme // so nothing accidentally reactivates Sunlight on the next visit. try { localStorage.setItem("neko-theme", NEKO_THEME_LOCK || theme); } catch (e) {} }, [theme]); const setTheme = (t) => { if (NEKO_THEME_LOCK) return; setThemeRaw(t); }; return [theme, setTheme]; } // --------------------------------------------------------------- // Shared design tweaks — persisted in localStorage (cross-page) // --------------------------------------------------------------- const NEKO_TWEAK_DEFAULTS = { navVariant: "B", // A: Saray Camı, B: Kale Burcu (aktif), C: Yüzen Adalar cardVariant: "auto", // auto: kategoriye göre A/B/C — A/B/C: tek tip taslak varyant heroVariant: "1", // 1: Sinematik Merkez (aktif), 2: Bölünmüş Çerçeve headingFont: "minecraft", // minecraft | pixelify glassBlur: 18, // px — liquid-glass varsayılanı (katmanlı cam hissi geri yüklendi) decor: "2", // 0 yok, 1 az, 2 normal }; // One-time migration: the active site now uses Hero 1 + Navbar B and // category-based product cards ("auto"). Older saved tweak values are // re-pointed once; the variants themselves remain selectable as drafts. // v4: glassBlur'u 0'dan 18'e taşı (liquid-glass geri yüklendi); tema kilidi // Midnight. Eski kayıtlar bir kez yeniden eşlenir; varyantlar taslak kalır. const NEKO_TWEAKS_VERSION = 4; function nekoReadTweaks() { let saved = {}; try { saved = JSON.parse(localStorage.getItem("neko-tweaks") || "{}"); } catch (e) { saved = {}; } if (saved.__v !== NEKO_TWEAKS_VERSION) { saved = { ...saved, navVariant: "B", heroVariant: "1", cardVariant: "auto", glassBlur: 18, __v: NEKO_TWEAKS_VERSION }; try { localStorage.setItem("neko-tweaks", JSON.stringify(saved)); } catch (e) {} } return { ...NEKO_TWEAK_DEFAULTS, ...saved }; } function useNekoTweaks() { const [tw, setTwState] = React.useState(nekoReadTweaks); const setTw = (key, val) => { setTwState((p) => { const next = { ...p, [key]: val, __v: NEKO_TWEAKS_VERSION }; try { localStorage.setItem("neko-tweaks", JSON.stringify(next)); } catch (e) {} return next; }); }; // Side effects: fonts, glass intensity, decor density React.useEffect(() => { const root = document.documentElement; root.style.setProperty("--font-hero", tw.headingFont === "minecraft" ? '"MinecraftTTF", "Pixelify Sans", monospace' : '"Pixelify Sans", monospace'); root.style.setProperty("--glass-blur", "blur(" + tw.glassBlur + "px) saturate(150%)"); root.style.setProperty("--frost-blur", "blur(" + Math.max(6, Math.round(tw.glassBlur * 0.6)) + "px) saturate(135%)"); document.body.classList.remove("decor-0", "decor-1", "decor-2"); document.body.classList.add("decor-" + tw.decor); // Bulanıklık düşükken cam yüzeyler okunabilirliğini kenar/parlama/opaklıkla korur document.body.classList.toggle("glass-flat", (tw.glassBlur || 0) < 6); }, [tw]); return [tw, setTw]; } // --------------------------------------------------------------- // Toast // --------------------------------------------------------------- function nekoToast(msg) { window.dispatchEvent(new CustomEvent("neko:toast", { detail: msg })); } function ToastHost() { const [msg, setMsg] = React.useState(null); React.useEffect(() => { let t; const on = (e) => { setMsg(e.detail); clearTimeout(t); t = setTimeout(() => setMsg(null), 2200); }; window.addEventListener("neko:toast", on); return () => { window.removeEventListener("neko:toast", on); clearTimeout(t); }; }, []); if (!msg) return null; return (
{sub}
: null}