(function (global) { "use strict"; const DEFAULT_THEMES = [ { id: "light", label: "轻量", path: "index.html" }, { id: "cyberpunk", label: "赛博", path: "cyberpunk/index.html" }, { id: "macos", label: "macOS", path: "macos/index.html" }, { id: "matrix", label: "Matrix", path: "matrix/index.html" }, { id: "notion", label: "Notion", path: "notion/index.html" }, { id: "synthwave", label: "蒸汽波", path: "synthwave/index.html" }, { id: "exe", label: "EXE", path: "exe/index.html" }, ]; function resolveThemeUrl(targetPath, currentPath) { try { const url = new URL(global.location.href); if (url.pathname.endsWith(currentPath)) { url.pathname = url.pathname.slice(0, -currentPath.length) + targetPath; return url.toString(); } return new URL(targetPath, global.location.href).toString(); } catch (error) { return targetPath; } } function initStyleSwitcher(config) { const host = document.querySelector(config.hostSelector || "[data-style-switcher]"); if (!host) { return; } const themes = config.themes || DEFAULT_THEMES; const wrapper = document.createElement("div"); wrapper.className = "style-switcher"; const label = document.createElement("label"); label.className = "style-switcher-label"; label.textContent = config.switcherLabel || "风格"; const select = document.createElement("select"); select.className = "style-switcher-select"; select.setAttribute( "aria-label", config.switcherAriaLabel || "切换界面风格", ); themes.forEach((theme) => { const option = document.createElement("option"); option.value = resolveThemeUrl(theme.path, config.currentThemePath); option.textContent = theme.label; option.selected = theme.id === config.themeId; select.appendChild(option); }); select.addEventListener("change", () => { global.location.href = select.value; }); wrapper.appendChild(label); wrapper.appendChild(select); host.replaceChildren(wrapper); } function getElement(id) { return document.getElementById(id); } function initDiffPage(config) { const leftTextarea = getElement(config.ids?.leftTextarea || "leftTextarea"); const rightTextarea = getElement( config.ids?.rightTextarea || "rightTextarea", ); const compareBtn = getElement(config.ids?.compareBtn || "compareBtn"); const swapBtn = getElement(config.ids?.swapBtn || "swapBtn"); const exampleBtn = getElement(config.ids?.exampleBtn || "exampleBtn"); const clearLeftBtn = getElement(config.ids?.clearLeftBtn || "clearLeftBtn"); const clearRightBtn = getElement( config.ids?.clearRightBtn || "clearRightBtn", ); const languageSelect = getElement( config.ids?.languageSelect || "languageSelect", ); const diffOutput = getElement(config.ids?.diffOutput || "diff-output"); const diffStats = getElement(config.ids?.diffStats || "diffStats"); const viewOptions = document.querySelectorAll( config.selectors?.viewOptions || ".view-option", ); if ( !leftTextarea || !rightTextarea || !compareBtn || !swapBtn || !exampleBtn || !clearLeftBtn || !clearRightBtn || !languageSelect || !diffOutput || !diffStats || viewOptions.length === 0 ) { throw new Error("Diff page initialization failed: missing required DOM nodes."); } const messages = config.messages || {}; const example = config.example || {}; const normalizeLanguage = config.normalizeLanguage || ((language) => language); const formatStats = config.formatStats || ((result) => { if (result.identical) { return "No changes"; } return `+${result.added} / -${result.deleted}`; }); let currentView = config.initialView || Array.from(viewOptions).find((button) => button.classList.contains("active")) ?.getAttribute("data-view") || "side-by-side"; function setOutput(html, statsHtml) { diffOutput.innerHTML = html; diffStats.innerHTML = statsHtml || ""; } function getSelectedLanguage() { return normalizeLanguage(languageSelect.value); } function getHighlightConfig() { const language = getSelectedLanguage(); if (language === "plaintext") { return false; } return { enabled: true, language: language }; } function generateUnifiedDiff(original, modified) { return Diff.createTwoFilesPatch( config.fileLabels?.left || "Original", config.fileLabels?.right || "Modified", original || "", modified || "", "", "", { context: config.contextLines || 4 }, ); } function applyHighlighting() { const selectedLanguage = getSelectedLanguage(); if (selectedLanguage === "plaintext" || !global.hljs) { return; } const codeBlocks = diffOutput.querySelectorAll("code"); codeBlocks.forEach((block) => { block.classList.forEach((className) => { if (className.startsWith("language-")) { block.classList.remove(className); } }); block.classList.add("language-" + selectedLanguage); delete block.dataset.highlighted; try { global.hljs.highlightElement(block); } catch (error) {} }); } function renderDiff() { let diffString = ""; try { diffString = generateUnifiedDiff(leftTextarea.value, rightTextarea.value); } catch (error) { setOutput( typeof messages.generateError === "function" ? messages.generateError(error) : "
Diff generation failed.
", messages.generateErrorStats || "", ); return; } let diffHtml = ""; try { diffHtml = Diff2Html.html(diffString, { drawFileList: false, matching: "lines", outputFormat: currentView, highlight: getHighlightConfig(), renderNothingWhenEmpty: false, }); } catch (error) { setOutput( typeof messages.renderError === "function" ? messages.renderError(error) : "
Diff rendering failed.
", messages.renderErrorStats || "", ); return; } diffOutput.innerHTML = diffHtml; applyHighlighting(); const added = diffOutput.querySelectorAll(".d2h-ins").length; const deleted = diffOutput.querySelectorAll(".d2h-del").length; const identical = added === 0 && deleted === 0; diffStats.innerHTML = formatStats({ added: added, deleted: deleted, identical: identical, }); if (!diffHtml.trim() || diffOutput.innerText.trim() === "") { setOutput(messages.blankResult || "
Files are identical.
", messages.blankStats || ""); } } function setActiveView(view, shouldRender) { currentView = view; viewOptions.forEach((button) => { button.classList.toggle( "active", button.getAttribute("data-view") === view, ); }); if (shouldRender !== false) { renderDiff(); } } function loadExample() { leftTextarea.value = example.left || ""; rightTextarea.value = example.right || ""; if (example.language) { languageSelect.value = example.language; } renderDiff(); } function swapTexts() { const currentLeft = leftTextarea.value; leftTextarea.value = rightTextarea.value; rightTextarea.value = currentLeft; renderDiff(); } function clearLeft() { leftTextarea.value = ""; renderDiff(); } function clearRight() { rightTextarea.value = ""; renderDiff(); } compareBtn.addEventListener("click", renderDiff); swapBtn.addEventListener("click", swapTexts); exampleBtn.addEventListener("click", loadExample); clearLeftBtn.addEventListener("click", clearLeft); clearRightBtn.addEventListener("click", clearRight); languageSelect.addEventListener("change", renderDiff); viewOptions.forEach((button) => { button.addEventListener("click", (event) => { setActiveView(event.currentTarget.getAttribute("data-view")); }); }); document.addEventListener("keydown", (event) => { if ((event.ctrlKey || event.metaKey) && event.key === "Enter") { event.preventDefault(); renderDiff(); } }); initStyleSwitcher(config); setActiveView(currentView, false); renderDiff(); } global.initDiffPage = initDiffPage; })(window);