// 全站自定义 CSS:SSR * * 常用令牌: * --bg / --panel / --ink / --accent * --bg-halo 顶部光晕色(建议 ≤ 0.04) * --bg-noise-opacity 噪点层透明度 * --r / --shadow-sm 卡片圆角与阴影 */ /* 改底色与光晕(浅 / 暗成对改) :root { --bg: #f7f7f5; --bg-halo: rgba(7, 192, 95, 0.04); --bg-noise-opacity: 0.03; --panel: #ffffff; --r: 12px; --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05); } .dark { --bg: #101211; --bg-halo: rgba(7, 192, 95, 0.03); --bg-noise-opacity: 0.04; } */ /* 关掉光晕和噪点 body::before, body::after { display: none; } */ /* 隐藏页脚 footer { display: none; } */ `; export const SAMPLE_CUSTOM_JS = `// 姜十三 · 自定义 JS 示例 // 仅整页加载执行一次(F5 / 首次进入),站内软导航不会重跑。 // 勿写入 // // 给 打标记,便于 CSS 写 html[data-custom] .panel { ... } // document.documentElement.dataset.custom = "1"; // // 等 DOM 就绪后再改节点 // document.addEventListener("DOMContentLoaded", function () { // // document.querySelector("footer")?.remove(); // }); `; const SSR_STYLE_ID = "j13-custom-css"; const RUNTIME_STYLE_ID = "j13-custom-css-runtime"; export function clipCustomSnippet(raw: string, max: number): string { const s = (raw || "").replace(/\u0000/g, ""); if (s.length <= max) return s; return [...s].slice(0, max).join(""); } function ensureRuntimeStyle(): HTMLStyleElement { let el = document.getElementById(RUNTIME_STYLE_ID) as HTMLStyleElement | null; if (el) return el; el = document.createElement("style"); el.id = RUNTIME_STYLE_ID; el.setAttribute("data-custom-css-source", "runtime"); document.body.appendChild(el); return el; } function removeRuntimeStyle(): void { document.getElementById(RUNTIME_STYLE_ID)?.remove(); } function ssrStyle(): HTMLStyleElement | null { return document.getElementById(SSR_STYLE_ID) as HTMLStyleElement | null; } /** 将自定义 CSS 应用到当前文档。preview=true 时忽略 SSR 节点(外观页草稿)。 */ export function applyCustomCSS(css: string, preview = false): void { if (typeof document === "undefined") return; const text = clipCustomSnippet(css, MAX_CUSTOM_CSS_CHARS).trim(); const ssr = ssrStyle(); if (ssr) ssr.disabled = preview; if (!text) { removeRuntimeStyle(); if (!preview && ssr) ssr.disabled = false; return; } ensureRuntimeStyle().textContent = text; }