|
<!DOCTYPE html> |
|
<html lang="zh"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
<title>CodeDiff</title> |
|
<script> |
|
const savedConfig=localStorage.getItem("codeDiffConfig"),theme=savedConfig?JSON.parse(savedConfig).theme:"vs-dark";document.documentElement.style.colorScheme="vs"===theme?"light":"dark",document.documentElement.dataset.theme=theme; |
|
</script> |
|
<style> |
|
*{margin:0;padding:0;box-sizing:border-box}body{overflow:hidden}:root[data-theme=vs]{background-color:#fff}:root[data-theme=vs-dark]{background-color:#1e1e1e}:root[data-theme=hc-black]{background-color:#000}.toolbar{position:fixed;top:0;left:0;right:0;padding:10px;background:#1e1e1e;z-index:100;display:flex;gap:10px;align-items:center}:root[data-theme=vs] .toolbar{background:#f3f3f3}:root[data-theme=vs-dark] .toolbar{background:#1e1e1e}:root[data-theme=hc-black] .toolbar{background:#000}:root[data-theme=vs] .toolbar button,:root[data-theme=vs] .toolbar input,:root[data-theme=vs] .toolbar select{background:#fff;color:#000;border:1px solid #ccc;border-radius:4px}:root[data-theme=vs] .toolbar button:hover,:root[data-theme=vs] .toolbar select:hover{background:#e8e8e8}:root[data-theme=vs-dark] .toolbar button,:root[data-theme=vs-dark] .toolbar input,:root[data-theme=vs-dark] .toolbar select{background:#3c3c3c;color:#fff;border:1px solid #555;border-radius:4px}:root[data-theme=vs-dark] .toolbar button:hover,:root[data-theme=vs-dark] .toolbar select:hover{background:#4c4c4c}:root[data-theme=hc-black] .toolbar button,:root[data-theme=hc-black] .toolbar input,:root[data-theme=hc-black] .toolbar select{background:#000;color:#fff;border:1px solid #fff;border-radius:4px}:root[data-theme=hc-black] .toolbar button:hover,:root[data-theme=hc-black] .toolbar select:hover{background:#333}:root[data-theme=vs] .toolbar .separator{background:#ccc}:root[data-theme=vs-dark] .toolbar .separator{background:#555}:root[data-theme=hc-black] .toolbar .separator{background:#fff}.toolbar button,.toolbar input,.toolbar select{padding:5px 10px;cursor:pointer;transition:background-color .2s}.toolbar input[type=number]::-webkit-inner-spin-button,.toolbar input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.toolbar input[type=number]{-moz-appearance:textfield;width:50px;text-align:center}:root[data-theme=vs] .shortcut-help{background:rgba(255,255,255,.9);color:#000;border:1px solid #ccc}:root[data-theme=vs-dark] .shortcut-help{background:rgba(30,30,30,.9);color:#fff;border:1px solid #555}:root[data-theme=hc-black] .shortcut-help{background:#000;color:#fff;border:1px solid #fff}.toolbar button,.toolbar input,.toolbar select{padding:5px 10px;background:#3c3c3c;color:#fff;border:1px solid #555;border-radius:4px;cursor:pointer}.toolbar button:hover{background:#4c4c4c}.toolbar .separator{width:1px;height:24px;background:#555;margin:0 10px}.toolbar .font-size-control{display:flex;align-items:center;gap:5px}.toolbar .font-size-control input{width:50px;text-align:center}.toolbar input[type=number]::-webkit-inner-spin-button,.toolbar input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.toolbar input[type=number]{-moz-appearance:textfield}#editor-main{width:100vw;height:calc(100vh - 50px);position:fixed;top:50px;left:0;overflow:hidden}.shortcut-help{position:fixed;bottom:20px;right:20px;background:rgba(30,30,30,.9);padding:15px;border-radius:8px;color:#fff;font-size:14px;display:none;z-index:1000}.shortcut-help.visible{display:block}.zen-mode .toolbar{display:none}#editor-main.zen-mode{height:100vh;top:0} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<div class="toolbar"> |
|
<select id="language-select"> |
|
<option value="plaintext">Plain Text</option> |
|
<option value="python">Python</option> |
|
<option value="cpp">C++</option> |
|
<option value="java">Java</option> |
|
<option value="c">C</option> |
|
<option value="javascript">JavaScript</option> |
|
<option value="go">Go</option> |
|
<option value="rust">Rust</option> |
|
<option value="sql">SQL</option> |
|
<option value="matlab">MATLAB</option> |
|
<option value="ruby">Ruby</option> |
|
<option value="r">R</option> |
|
<option value="swift">Swift</option> |
|
<option value="html">HTML</option> |
|
<option value="css">CSS</option> |
|
<option value="csharp">C#</option> |
|
<option value="typescript">TypeScript</option> |
|
<option value="php">PHP</option> |
|
<option value="kotlin">Kotlin</option> |
|
<option value="shell">Shell</option> |
|
<option value="scala">Scala</option> |
|
<option value="perl">Perl</option> |
|
<option value="lua">Lua</option> |
|
<option value="json">JSON</option> |
|
<option value="yaml">YAML</option> |
|
<option value="xml">XML</option> |
|
<option value="markdown">Markdown</option> |
|
<option value="dockerfile">Dockerfile</option> |
|
<option value="ini">INI</option> |
|
</select> |
|
<div class="separator"></div> |
|
<button id="view-mode-toggle" title="切换对比模式">对比模式</button> |
|
<button id="theme-toggle" title="">切换主题</button> |
|
<button id="line-numbers-toggle" title="">切换行号</button> |
|
<button id="zen-mode-toggle" title="">Zen模式</button> |
|
<div class="separator"></div> |
|
<div class="font-size-control"> |
|
<button id="font-decrease" title="">A-</button> |
|
<input type="number" id="font-size" value="14" min="8" max="32" title="字体大小"> |
|
<button id="font-increase" title="">A+</button> |
|
</div> |
|
<div class="separator"></div> |
|
<button id="copy-left" title="">复制左侧</button> |
|
<button id="copy-right" title="">复制右侧</button> |
|
<button id="clear" title="">清空</button> |
|
<div class="separator"></div> |
|
<button id="help-toggle" title="">快捷键帮助</button> |
|
</div> |
|
<div id="editor-main"></div> |
|
<div class="shortcut-help"> |
|
<h3>快捷键列表</h3> |
|
<br> |
|
<div>ESC: 退出 Zen 模式<br></div> |
|
</div> |
|
|
|
<script src="https://fastly.jsdelivr.net/npm/monaco-editor@latest/min/vs/loader.js"></script> |
|
<script> |
|
const userPreferences={get:function(){const e=localStorage.getItem("codeDiffConfig");return e?JSON.parse(e):{language:"plaintext",theme:"vs-dark",fontSize:14,lineNumbers:!0,zenMode:!1,inlineView:!1}},save:function(e){localStorage.setItem("codeDiffConfig",JSON.stringify(e))},update:function(e,t){const n=this.get();n[e]=t,this.save(n)}};require.config({paths:{vs:"https://fastly.jsdelivr.net/npm/monaco-editor@latest/min/vs"}}),require(["vs/editor/editor.main"],(function(){const e=userPreferences.get(),t=["vs","vs-dark","hc-black"],n={vs:"浅色","vs-dark":"深色","hc-black":"高对比度"},o={vs:{"diffEditor.insertedTextBackground":"#97f295aa","diffEditor.removedTextBackground":"#ffb6b6aa","diffEditor.insertedLineBackground":"#c3e6c370","diffEditor.removedLineBackground":"#ffd7d770","diffEditorGutter.insertedLineBackground":"#87cf85","diffEditorGutter.removedLineBackground":"#ff9494"},"vs-dark":{"diffEditor.insertedTextBackground":"#376D3799","diffEditor.removedTextBackground":"#93404099","diffEditor.insertedLineBackground":"#376D3744","diffEditor.removedLineBackground":"#93404044","diffEditorGutter.insertedLineBackground":"#2ea043","diffEditorGutter.removedLineBackground":"#f85149"},"hc-black":{"diffEditor.insertedTextBackground":"#37d33ccc","diffEditor.removedTextBackground":"#ff4444cc","diffEditor.insertedLineBackground":"#37d33c99","diffEditor.removedLineBackground":"#ff444499","diffEditorGutter.insertedLineBackground":"#37d33c","diffEditorGutter.removedLineBackground":"#ff4444"}};t.forEach((e=>{monaco.editor.defineTheme(`custom-${e}`,{base:e,inherit:!0,rules:[],colors:o[e]})})),monaco.editor.setTheme(`custom-${e.theme}`);const d={language:e.language,originalEditable:!0,fontSize:e.fontSize,wordWrap:"on",automaticLayout:!0,theme:e.theme,fontFamily:'"JetBrains Mono","HarmonyOS Sans SC","Cascadia Code","Consolas","Menlo","monospace"',minimap:{enabled:!0},scrollbar:{vertical:"visible",horizontal:"visible"},renderSideBySide:!e.inlineView,lineNumbers:e.lineNumbers?"on":"off",diffWordWrap:"on",renderIndicators:!0,renderOverviewRuler:!0,diffAlgorithm:"advanced",bracketPairColorization:{enabled:!0},autoClosingBrackets:"always",autoClosingQuotes:"always",formatOnPaste:!0,formatOnType:!0,tabSize:4,scrollBeyondLastLine:!1};let i=monaco.editor.createDiffEditor(document.getElementById("editor-main"),d),r=e.zenMode;i.setModel({original:monaco.editor.createModel("",e.language),modified:monaco.editor.createModel("",e.language)}),document.getElementById("language-select").value=e.language,document.getElementById("font-size").value=e.fontSize,e.zenMode&&s(),document.getElementById("language-select").addEventListener("change",(e=>{const t=e.target.value,n=i.getModel();monaco.editor.setModelLanguage(n.original,t),monaco.editor.setModelLanguage(n.modified,t),userPreferences.update("language",t)}));let a=e.inlineView;function c(){document.getElementById("view-mode-toggle").textContent="对比模式: "+(a?"内联":"并排")}c(),document.getElementById("view-mode-toggle").addEventListener("click",(function(){a=!a,i.updateOptions({renderSideBySide:!a}),userPreferences.update("inlineView",a),c()})),document.getElementById("theme-toggle").textContent=`主题: ${n[e.theme]}`;let l=e.lineNumbers;function s(){r=!r,document.body.classList.toggle("zen-mode",r),document.getElementById("editor-main").classList.toggle("zen-mode",r),i.layout(),userPreferences.update("zenMode",r)}let u=e.fontSize;function m(e){u=Math.max(8,Math.min(32,e)),document.getElementById("font-size").value=u,i.updateOptions({fontSize:u}),userPreferences.update("fontSize",u)}let g=!1;async function f(e){try{await navigator.clipboard.writeText(e)}catch(e){console.error("复制失败:",e)}}document.getElementById("theme-toggle").addEventListener("click",(function(){const e=userPreferences.get().theme,o=t.indexOf(e),d=t[(o+1)%t.length];monaco.editor.setTheme(`custom-${d}`),document.documentElement.dataset.theme=d,document.documentElement.style.colorScheme="vs"===d?"light":"dark",userPreferences.update("theme",d),document.getElementById("theme-toggle").textContent=`主题: ${n[d]}`})),document.getElementById("line-numbers-toggle").addEventListener("click",(function(){l=!l,i.updateOptions({lineNumbers:l?"on":"off"}),userPreferences.update("lineNumbers",l)})),document.getElementById("zen-mode-toggle").addEventListener("click",s),document.getElementById("font-decrease").addEventListener("click",(()=>m(u-2))),document.getElementById("font-increase").addEventListener("click",(()=>m(u+2))),document.getElementById("font-size").addEventListener("change",(e=>m(parseInt(e.target.value)))),document.getElementById("copy-left").addEventListener("click",(()=>f(i.getModel().original.getValue()))),document.getElementById("copy-right").addEventListener("click",(()=>f(i.getModel().modified.getValue()))),document.getElementById("zen-mode-toggle").title="Zen模式 (ESC退出)",document.getElementById("help-toggle").addEventListener("click",(function(){g=!g,document.querySelector(".shortcut-help").classList.toggle("visible",g)})),document.getElementById("clear").addEventListener("click",(()=>{i.getModel().original.setValue(""),i.getModel().modified.setValue("")})),function(){const e=document.getElementById("font-size");e.addEventListener("wheel",(t=>{t.preventDefault();const n=t.deltaY<0?1:-1;m(parseInt(e.value)+n)}))}(),window.addEventListener("keydown",(e=>{"Escape"===e.key&&r&&s(),e.ctrlKey&&"s"===e.key&&e.preventDefault()}))})); |
|
</script> |
|
</body> |
|
</html> |