Notebook / index.html
SolarumAsteridion's picture
Update index.html
c744306 verified
<!DOCTYPE html>
<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>