Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> | |
| <title>LaTeX Notepad</title> | |
| <!-- ──────── MathJax ──────── --> | |
| <script> | |
| window.MathJax = { | |
| tex: { | |
| inlineMath: [['$', '$'], ['\\(', '\\)']], | |
| displayMath: [['$$', '$$'], ['\\[', '\\]']], | |
| processEscapes: true, | |
| processEnvironments: true, | |
| }, | |
| options: { skipHtmlTags: ['script','noscript','style','textarea','pre'] } | |
| }; | |
| </script> | |
| <script | |
| id="MathJax-script" | |
| async | |
| src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" | |
| ></script> | |
| <!-- marked.js for Markdown --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <!-- Google fonts --> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=Crimson+Text:wght@400;600&family=Libre+Baskerville:wght@400;700&family=PT+Mono&display=swap" | |
| rel="stylesheet" | |
| /> | |
| <style> | |
| /* ───────────────────────────────────────── | |
| COLOR SYSTEM (light / dark via variables) | |
| ───────────────────────────────────────── */ | |
| :root { | |
| --desk-bg: #fbf9f5; | |
| --desk-dot: #e2dccd; | |
| --paper-bg: #fffefa; | |
| --paper-text: #222; | |
| --shadow: rgba(0,0,0,.35); | |
| --line: rgba(201,190,170,.18); | |
| --perforation: #d0c6b7; | |
| --tbl-border: #d7cebf; | |
| --blockquote-bg: #fbf8f1; | |
| --blockquote-bar: #ccbfae; | |
| --code-bg: #f4f2ec; | |
| --code-border: #e6e0d2; | |
| --inline-code-bg: #f2efe8; | |
| } | |
| body.dark { | |
| --desk-bg: #2c2a27; | |
| --desk-dot: #3a3733; | |
| --paper-bg: #302e2b; | |
| --paper-text: #e9e7e2; | |
| --shadow: rgba(0,0,0,.55); | |
| --line: rgba(110,103,94,.28); | |
| --perforation: #6d6456; | |
| --tbl-border: #555048; | |
| --blockquote-bg: #38332e; | |
| --blockquote-bar: #6a604e; | |
| --code-bg: #3a3530; | |
| --code-border: #514b42; | |
| --inline-code-bg: #4a443d; | |
| } | |
| /* ───────────────────────────────────────── | |
| GLOBAL “DESK” BACKGROUND | |
| ───────────────────────────────────────── */ | |
| html,body{height:100%} | |
| body{ | |
| margin:0; | |
| background: var(--desk-bg); | |
| background-image: radial-gradient(var(--desk-dot) 1px,transparent 1px); | |
| background-size:14px 14px; | |
| font-family:'Crimson Text','Times New Roman',serif; | |
| color:var(--paper-text); | |
| line-height:1.8; | |
| -webkit-font-smoothing:antialiased; | |
| } | |
| /* ───────────────────────────────────────── | |
| PAPER SHEET | |
| ───────────────────────────────────────── */ | |
| .container{ | |
| max-width:840px; | |
| margin:40px auto; | |
| padding:40px 60px 60px; | |
| background:var(--paper-bg); | |
| color:var(--paper-text); | |
| border:1px solid rgba(0,0,0,.05); | |
| border-radius:12px 12px 10px 10px; | |
| position:relative; | |
| box-shadow:0 18px 40px -22px var(--shadow), | |
| inset 0 2px 6px rgba(0,0,0,.06); | |
| background-size:160px 160px,100% 100%; | |
| } | |
| /* perforation holes */ | |
| .container::before{ | |
| content:''; | |
| position:absolute;top:26px;bottom:26px;left:30px;width:9px; | |
| background-image:radial-gradient(circle var(--perforation) 0%,var(--perforation) 2px,transparent 3px); | |
| background-size:9px 28px; | |
| background-repeat:repeat-y; | |
| pointer-events:none; | |
| } | |
| /* curled corner */ | |
| .container::after{ | |
| content:'';position:absolute;top:0;right:0;width:110px;height:110px; | |
| background: | |
| linear-gradient(135deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 42%), | |
| linear-gradient(135deg,var(--paper-bg) 0%,var(--paper-bg) 50%,rgba(255,255,255,0) 51%); | |
| background-size:100% 100%; | |
| border-bottom-left-radius:12px; | |
| transform:translate(1px,-1px); | |
| pointer-events:none; | |
| } | |
| /* dark-mode gradient uses the *same* var so stays consistent */ | |
| /* ───────── theme toggle ───────── */ | |
| #themeToggle{ | |
| position:absolute;top:12px;right:14px; | |
| font-size:20px;background:none;border:none;cursor:pointer; | |
| transition:transform .25s; | |
| user-select:none; | |
| } | |
| /* put this anywhere after the existing #themeToggle rule */ | |
| #themeToggle{ | |
| position:absolute; /* you already have this */ | |
| z-index:10; /* NEW – lift it above the curl */ | |
| } | |
| #themeToggle:hover{transform:rotate(20deg)scale(1.15)} | |
| /* ───────── header ───────── */ | |
| .header{text-align:center;margin-bottom:34px;padding-bottom:18px;border-bottom:1px solid rgba(0,0,0,.05)} | |
| h1{font-family:'Libre Baskerville',serif;margin:0;font-size:30px;letter-spacing:.5px} | |
| .subtitle{font-family:'PT Mono',monospace;font-size:14px;color:#666;margin-top:6px;letter-spacing:1px} | |
| /* ───────── content area ───────── */ | |
| #content{ | |
| min-height:520px;font-size:18px;position:relative; | |
| padding:10px 0 10px 26px;overflow-wrap:break-word;hyphens:auto; | |
| } | |
| #content::before{ | |
| content:'';position:absolute;inset:0; | |
| background:repeating-linear-gradient( | |
| 0deg, | |
| transparent,transparent 2.65em, | |
| var(--line) 2.65em,var(--line) 2.7em); | |
| pointer-events:none;z-index:1; | |
| } | |
| #content *{position:relative;z-index:2} | |
| .placeholder{color:#888;font-style:italic;text-align:center;padding:110px 20px;user-select:none} | |
| /* ───────── markdown tweaks ───────── */ | |
| blockquote{ | |
| border-left:4px solid var(--blockquote-bar); | |
| margin:20px 0;padding:15px 26px; | |
| background:var(--blockquote-bg);font-style:italic | |
| } | |
| code{font-family:'PT Mono',monospace;background:var(--inline-code-bg); | |
| padding:2px 6px;border-radius:3px;font-size:.9em} | |
| pre{background:var(--code-bg);padding:16px 20px;border:1px solid var(--code-border); | |
| border-radius:6px;overflow-x:auto;font-family:'PT Mono',monospace} | |
| /* lists */ | |
| ol{counter-reset:item;padding-left:0;list-style:none} | |
| ol>li{counter-increment:item;margin:.5em 0 .5em 2em} | |
| ol>li::before{content:counter(item)')';display:inline-block;width:1.5em;margin-left:-2em;text-align:right;font-weight:600} | |
| ol ol>li::before{content:counter(item,lower-alpha)')'} | |
| /* ───────── TABLES ───────── */ | |
| table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums;margin:1.2em 0} | |
| thead tr{border-bottom:1px solid var(--tbl-border)} | |
| tbody tr:not(:last-child){border-bottom:1px solid var(--tbl-border)} | |
| th,td{padding:.55em .8em;text-align:right} | |
| th{font-weight:600} | |
| /* ───────── processing badge ───────── */ | |
| .processing{ | |
| position:fixed;top:20px;right:20px;background:#333;color:#fff; | |
| padding:10px 20px;border-radius:6px;font-family:'PT Mono',monospace; | |
| font-size:14px;opacity:0;transition:opacity .25s;z-index:2000 | |
| } | |
| .processing.show{opacity:.9} | |
| /* responsive & print */ | |
| @media(max-width:768px){ | |
| .container{margin:20px 16px;padding:28px} | |
| #content{font-size:16px} | |
| h1{font-size:24px} | |
| } | |
| @media print{ | |
| body{background:#fff} | |
| .container{box-shadow:none;border:none} | |
| .header,.processing,.instructions,#themeToggle{display:none} | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <!-- theme icon --> | |
| <button id="themeToggle" title="Toggle dark / light">🌙</button> | |
| <div class="header"> | |
| <h1>LaTeX Notepad</h1> | |
| <div class="subtitle">press ctrl+v anywhere to render</div> | |
| </div> | |
| <div id="content"> | |
| <div class="placeholder"> | |
| Press <kbd>Ctrl</kbd>+<kbd>V</kbd> (or <kbd>⌘</kbd>+<kbd>V</kbd>) to paste and render Markdown / LaTeX | |
| </div> | |
| </div> | |
| <div class="instructions" style="text-align:center;font-family:'PT Mono',monospace;font-size:14px;color:#666;font-style:italic;margin-top:22px;"> | |
| Tip: you can paste raw Markdown or TeX – it will be rendered instantly ✨ | |
| </div> | |
| </div> | |
| <div class="processing">Processing…</div> | |
| <script> | |
| /* ======= processing badge helpers ======= */ | |
| const content = document.getElementById('content'); | |
| const processingNode = document.querySelector('.processing'); | |
| function showProcessing(){processingNode.classList.add('show')} | |
| function hideProcessing(){setTimeout(()=>processingNode.classList.remove('show'),300)} | |
| /* ======= markdown + latex pipeline ======= */ | |
| function processContent(text){ | |
| showProcessing(); | |
| const store=[], PL=i=>`%%LATEX_${i}%%`; let idx=0; | |
| const keep=m=>(store.push(m),PL(idx++)); | |
| text = text | |
| .replace(/\\\[[\s\S]*?\\\]/g, keep) // \[ ... \] | |
| .replace(/\$\$[\s\S]*?\$\$/g, keep) // $$ ... $$ | |
| .replace(/\\\([\s\S]*?\\\)/g, keep) // \( ... \) | |
| .replace(/\$([^\$\n]+?)\$/g, keep); // $ ... $ | |
| let html = marked.parse(text); | |
| store.forEach((latex,i)=>{html=html.replaceAll(PL(i),latex)}); | |
| content.innerHTML = html; | |
| if(window.MathJax?.typesetPromise){ | |
| MathJax.typesetPromise([content]).then(hideProcessing) | |
| .catch(e=>{console.error('MathJax error:',e);hideProcessing()}); | |
| }else{hideProcessing()} | |
| } | |
| /* ======= paste listener ======= */ | |
| document.addEventListener('paste',e=>{ | |
| e.preventDefault(); | |
| const txt=e.clipboardData.getData('text/plain'); | |
| if(txt.trim())processContent(txt); | |
| }); | |
| /* small “bounce” on placeholder click */ | |
| content.addEventListener('click',()=>{ | |
| const ph=content.querySelector('.placeholder'); | |
| if(ph){ | |
| ph.style.transform='scale(.97)'; | |
| ph.style.transition='transform .12s'; | |
| setTimeout(()=>ph.style.transform='scale(1)',120); | |
| } | |
| }); | |
| /* smooth fade in */ | |
| document.addEventListener('DOMContentLoaded',()=>{ | |
| const sheet=document.querySelector('.container'); | |
| sheet.style.opacity='0'; | |
| setTimeout(()=>{sheet.style.transition='opacity .6s ease';sheet.style.opacity='1'},80); | |
| }); | |
| /* ======= theme toggler ======= */ | |
| const btn = document.getElementById('themeToggle'); | |
| const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); | |
| const savedTheme = localStorage.getItem('note-theme'); | |
| initTheme(); | |
| btn.addEventListener('click',()=>{ | |
| document.body.classList.toggle('dark'); | |
| updateIcon(); | |
| localStorage.setItem('note-theme',document.body.classList.contains('dark')?'dark':'light'); | |
| }); | |
| function initTheme(){ | |
| if(savedTheme){ | |
| document.body.classList.toggle('dark',savedTheme==='dark'); | |
| }else if(prefersDark.matches){ | |
| document.body.classList.add('dark'); | |
| } | |
| updateIcon(); | |
| } | |
| function updateIcon(){ | |
| btn.textContent=document.body.classList.contains('dark')?'☀️':'🌙'; | |
| } | |
| </script> | |
| </body> | |
| </html> |