Files
diff.tootaio.com/matrix/index.html
xiaomai e9fcd411a7 feat(theme): add style switcher and centralize diff logic
Extract duplicated diff rendering logic into shared/diff-page.js
Implement theme switcher component across all templates
2026-04-08 14:39:39 +08:00

480 lines
16 KiB
HTML

<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.5, user-scalable=yes"
/>
<title>MATRIX // DIFF_PROTOCOL</title>
<!-- Highlight.js 主题 (暗黑基础) -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
/>
<!-- Diff2Html 核心样式 -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/diff2html@3.4.47/bundles/css/diff2html.min.css"
/>
<!-- 引入极客等宽字体 -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap"
rel="stylesheet"
/>
<!-- 引入 Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 配置 Tailwind -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
matrix: {
bg: "#000000",
green: "#00FF41",
darkGreen: "#008F11",
darkestGreen: "#003B00",
red: "#FF003C",
darkRed: "#3B0000",
},
},
fontFamily: {
mono: ['"Share Tech Mono"', "monospace", "Courier New"],
},
},
},
};
</script>
<style type="text/tailwindcss">
@layer components {
/* 矩阵面板 */
.matrix-panel {
@apply bg-black/85 backdrop-blur-sm border border-matrix-darkGreen shadow-[0_0_10px_rgba(0,143,17,0.2)] relative;
}
/* 矩阵输入框 */
.matrix-input {
@apply bg-transparent text-matrix-green border-none outline-none resize-y placeholder:text-matrix-darkGreen/50 focus:shadow-[inset_0_0_20px_rgba(0,255,65,0.05)];
text-shadow: 0 0 2px rgba(0, 255, 65, 0.4);
}
/* 矩阵按钮 */
.matrix-btn {
@apply bg-transparent text-matrix-green border border-matrix-darkGreen px-4 py-1 font-mono uppercase tracking-widest hover:bg-matrix-green hover:text-black hover:shadow-[0_0_15px_#00FF41] transition-all duration-200 cursor-pointer;
}
/* 视图切换按钮 */
.view-option {
@apply px-3 py-1 text-matrix-darkGreen hover:text-matrix-green transition-colors;
}
.view-option.active {
@apply text-black bg-matrix-green shadow-[0_0_10px_#00FF41];
}
.style-switcher {
@apply flex items-center gap-2;
}
.style-switcher-label {
@apply text-sm tracking-widest text-matrix-darkGreen;
}
.style-switcher-select {
background: #000;
border: 1px solid #008f11;
color: #00ff41;
padding: 0.25rem 0.75rem;
font-family: "Share Tech Mono", monospace;
font-size: 0.875rem;
outline: none;
}
.style-switcher-select:hover,
.style-switcher-select:focus {
border-color: #00ff41;
box-shadow: 0 0 10px #00ff41;
}
}
/* ==========================================
全局 CRT 扫描线滤镜
========================================== */
body::after {
content: " ";
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background:
linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
linear-gradient(
90deg,
rgba(255, 0, 0, 0.06),
rgba(0, 255, 0, 0.02),
rgba(0, 0, 255, 0.06)
);
z-index: 999;
background-size:
100% 2px,
3px 100%;
pointer-events: none;
}
/* ==========================================
覆盖 Diff2Html 样式 (黑客帝国化)
========================================== */
.d2h-wrapper {
font-family: "Share Tech Mono", monospace !important;
color: #00ff41 !important;
}
.d2h-file-header {
display: none !important;
}
.d2h-file-wrapper {
border: none !important;
background: transparent !important;
margin-bottom: 0 !important;
}
.d2h-code-line-ctn {
@apply font-mono text-[15px] !important;
color: #00ff41 !important;
text-shadow: 0 0 2px rgba(0, 255, 65, 0.3);
}
/* 差异行背景色 */
.d2h-ins {
background-color: rgba(0, 59, 0, 0.8) !important;
border-color: #00ff41 !important;
color: #00ff41 !important;
}
.d2h-del {
background-color: rgba(59, 0, 0, 0.8) !important;
border-color: #ff003c !important;
color: #ff003c !important;
text-shadow: 0 0 2px rgba(255, 0, 60, 0.5);
}
/* 行号区域 */
.d2h-code-linenumber {
background-color: #000 !important;
border-color: #008f11 !important;
color: #008f11 !important;
}
.d2h-info {
background-color: #001100 !important;
color: #008f11 !important;
border-color: #008f11 !important;
}
.d2h-emptyplaceholder {
background-color: #000 !important;
border-color: #008f11 !important;
}
tbody {
border-color: #008f11 !important;
}
td {
border-color: #008f11 !important;
}
/* 覆盖 highlight.js 的默认颜色,强制偏绿 (保留部分语法高亮但统一色调) */
.hljs-keyword,
.hljs-built_in {
color: #fff !important;
text-shadow: 0 0 5px #fff;
}
.hljs-string,
.hljs-title {
color: #00ff41 !important;
}
.hljs-comment {
color: #008f11 !important;
font-style: italic;
}
/* 矩阵滚动条 */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: #000;
border-left: 1px solid #008f11;
}
::-webkit-scrollbar-thumb {
background: #003b00;
border: 1px solid #008f11;
}
::-webkit-scrollbar-thumb:hover {
background: #00ff41;
}
/* 选中文本颜色 */
::selection {
background: #00ff41;
color: #000;
}
</style>
</head>
<body
class="bg-matrix-bg font-mono p-4 md:p-8 min-h-screen flex flex-col items-center text-matrix-green selection:bg-matrix-green selection:text-black relative overflow-x-hidden"
>
<!-- HTML5 Canvas 矩阵数字雨背景 -->
<canvas id="matrix-canvas" class="fixed inset-0 z-[-1] opacity-40"></canvas>
<!-- 主窗口 -->
<div class="max-w-[1600px] w-full flex flex-col gap-6 z-10">
<!-- 标题区 -->
<header
class="border-b-2 border-matrix-darkGreen pb-4 flex justify-between items-end"
>
<div>
<h1
class="text-3xl md:text-4xl font-bold tracking-widest drop-shadow-[0_0_8px_#00FF41]"
>
WAKE_UP_NEO <span class="animate-pulse">_</span>
</h1>
<p class="text-matrix-darkGreen mt-2 tracking-[0.2em] text-sm">
SYSTEM.DIFF_PROTOCOL // V.1.0.0
</p>
</div>
<div
class="hidden md:block text-right text-xs text-matrix-darkGreen tracking-widest"
>
CONNECTION: SECURE<br />
ENCRYPTION: RSA-4096
</div>
</header>
<!-- 双栏输入区 -->
<div class="flex flex-col lg:flex-row gap-6">
<!-- 左侧面板 -->
<div class="flex-1 flex flex-col matrix-panel">
<div
class="flex items-center justify-between px-4 py-2 border-b border-matrix-darkGreen bg-matrix-darkestGreen/50"
>
<label class="text-sm tracking-widest flex items-center gap-2">
> SOURCE_FILE.TXT
</label>
<button
id="clearLeftBtn"
class="text-xs text-matrix-darkGreen hover:text-matrix-green transition-colors tracking-widest"
>
[ PURGE ]
</button>
</div>
<textarea
id="leftTextarea"
class="matrix-input w-full flex-1 min-h-[260px] h-[300px] p-4 text-[15px] leading-relaxed"
spellcheck="false"
>
function enterMatrix() {
console.log("Follow the white rabbit.");
return "Ignorance is bliss.";
}</textarea
>
</div>
<!-- 右侧面板 -->
<div class="flex-1 flex flex-col matrix-panel">
<div
class="flex items-center justify-between px-4 py-2 border-b border-matrix-darkGreen bg-matrix-darkestGreen/50"
>
<label class="text-sm tracking-widest flex items-center gap-2">
> TARGET_FILE.TXT
</label>
<button
id="clearRightBtn"
class="text-xs text-matrix-darkGreen hover:text-matrix-green transition-colors tracking-widest"
>
[ PURGE ]
</button>
</div>
<textarea
id="rightTextarea"
class="matrix-input w-full flex-1 min-h-[260px] h-[300px] p-4 text-[15px] leading-relaxed"
spellcheck="false"
>
function enterMatrix() {
console.log("There is no spoon.");
decodeConstruct();
return "I know Kung Fu.";
}</textarea
>
</div>
</div>
<!-- 工具栏 -->
<div
class="matrix-panel p-4 flex flex-wrap items-center justify-between gap-4"
>
<div class="flex flex-wrap items-center gap-6">
<!-- 语言选择 -->
<div class="flex items-center gap-3">
<label class="text-sm tracking-widest text-matrix-darkGreen"
>PARSER:</label
>
<select
id="languageSelect"
class="bg-black border border-matrix-darkGreen text-matrix-green px-3 py-1 text-sm outline-none cursor-pointer hover:border-matrix-green focus:border-matrix-green focus:shadow-[0_0_10px_#00FF41] transition-all appearance-none"
>
<option value="javascript" selected>JAVASCRIPT</option>
<option value="typescript">TYPESCRIPT</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="json">JSON</option>
<option value="python">PYTHON</option>
<option value="java">JAVA</option>
<option value="cpp">C++</option>
<option value="plaintext">RAW_TEXT</option>
</select>
</div>
<!-- 视图切换 -->
<div
class="flex items-center border border-matrix-darkGreen p-0.5"
id="viewToggle"
>
<button class="view-option active" data-view="side-by-side">
SPLIT
</button>
<button class="view-option" data-view="line-by-line">
UNIFIED
</button>
</div>
<div data-style-switcher></div>
</div>
<!-- 操作按钮 -->
<div class="flex gap-4 flex-wrap">
<button id="swapBtn" class="matrix-btn">[ INVERT ]</button>
<button id="exampleBtn" class="matrix-btn">
[ LOAD_SIMULATION ]
</button>
<button
id="compareBtn"
class="matrix-btn font-bold bg-matrix-darkestGreen hover:bg-matrix-green hover:text-black shadow-[0_0_10px_rgba(0,255,65,0.2)]"
>
[ EXECUTE_DIFF ]
</button>
</div>
</div>
<!-- 差异结果区域 -->
<div class="matrix-panel flex flex-col">
<div
class="px-4 py-2 border-b border-matrix-darkGreen bg-matrix-darkestGreen/50 flex items-center justify-between"
>
<span class="tracking-widest text-sm">> ANALYSIS_RESULT</span>
<span id="diffStats" class="text-xs tracking-wider"></span>
</div>
<div id="diff-output" class="overflow-x-auto min-h-[150px]">
<div
class="p-12 text-center text-matrix-darkGreen tracking-widest animate-pulse"
>
WAITING FOR COMMAND...
</div>
</div>
</div>
</div>
<!-- 依赖库 -->
<script src="https://cdn.jsdelivr.net/npm/diff@5.1.0/dist/diff.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/diff2html@3.4.47/bundles/js/diff2html.min.js"></script>
<!-- 矩阵数字雨 Canvas 脚本 -->
<script>
const canvas = document.getElementById("matrix-canvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const katakana =
"アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレゲゼデベペオォコソトノホモヨョロゴゾドボポヴッン";
const latin = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const nums = "0123456789";
const alphabet = katakana + latin + nums;
const fontSize = 16;
const columns = canvas.width / fontSize;
const rainDrops = [];
for (let x = 0; x < columns; x++) {
rainDrops[x] = 1;
}
const draw = () => {
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#0F0";
ctx.font = fontSize + "px monospace";
for (let i = 0; i < rainDrops.length; i++) {
const text = alphabet.charAt(
Math.floor(Math.random() * alphabet.length),
);
ctx.fillText(text, i * fontSize, rainDrops[i] * fontSize);
if (
rainDrops[i] * fontSize > canvas.height &&
Math.random() > 0.975
) {
rainDrops[i] = 0;
}
rainDrops[i]++;
}
};
setInterval(draw, 30);
window.addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
</script>
<script src="../shared/diff-page.js"></script>
<script>
window.initDiffPage({
themeId: "matrix",
currentThemePath: "matrix/index.html",
switcherLabel: "MODE:",
switcherAriaLabel: "Switch Matrix skin",
fileLabels: {
left: "SOURCE",
right: "TARGET",
},
example: {
left: `function enterMatrix() {\n console.log("Follow the white rabbit.");\n return "Ignorance is bliss.";\n}`,
right: `function enterMatrix() {\n console.log("There is no spoon.");\n decodeConstruct();\n return "I know Kung Fu.";\n}`,
language: "javascript",
},
messages: {
generateError: (error) =>
`<div class="p-12 text-center text-matrix-red tracking-widest">FATAL_ERROR: ${error.message}</div>`,
renderError: () =>
'<div class="p-12 text-center text-matrix-red tracking-widest">RENDER_FAILURE</div>',
blankResult:
'<div class="p-12 text-center text-matrix-darkGreen tracking-widest">DATA_STREAMS_ARE_IDENTICAL.</div>',
blankStats: "<span class='text-matrix-darkGreen'>SYNCED</span>",
generateErrorStats: "ERR",
renderErrorStats: "ERR",
},
formatStats: ({ added, deleted, identical }) => {
if (identical) {
return "<span class='text-matrix-darkGreen'>MATCH_FOUND // NO_ANOMALIES</span>";
}
return `<span class='text-matrix-green'>+${added} INJECTED</span> <span class='text-matrix-darkGreen mx-2'>|</span> <span class='text-matrix-red'>-${deleted} PURGED</span>`;
},
});
</script>
</body>
</html>