Notebook / index.html
SolarumAsteridion's picture
Update index.html
7e995cd verified
raw
history blame
8.81 kB
<!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 -->
<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>
/* ----------- GLOBAL “DESK” BACKGROUND ---------- */
html,
body {
height: 100%;
}
body {
margin: 0;
background: #fbf9f5;
background-image: radial-gradient(#e2dccd 1px, transparent 1px);
background-size: 14px 14px;
font-family: 'Crimson Text', 'Times New Roman', serif;
color: #222;
line-height: 1.8;
-webkit-font-smoothing: antialiased;
}
/* ------------- PAPER SHEET -------------------- */
.container {
max-width: 840px;
margin: 40px auto;
padding: 40px 60px 60px;
background: #fffefa; /* warm paper colour */
border-radius: 12px 12px 10px 10px;
position: relative;
border: 1px solid #ede7db;
/* subtle inset shadow (feels like a notebook page) */
box-shadow:
0 18px 40px -22px rgba(0, 0, 0, 0.35),
inset 0 2px 6px rgba(0, 0, 0, 0.06);
/* tiny surface grain */
background-image:
url('data:image/svg+xml;utf8,\
<svg xmlns="http://www.w3.org/2000/svg" width=\"160\" height=\"160\" viewBox=\"0 0 40 40\" fill=\"%23dcd2c3\" opacity=\"0.35\">\
<circle cx=\"1\" cy=\"1\" r=\"0.5\"/><circle cx=\"13\" cy=\"7\" r=\"0.4\"/>\
<circle cx=\"27\" cy=\"4\" r=\"0.6\"/><circle cx=\"37\" cy=\"14\" r=\"0.5\"/>\
<circle cx=\"20\" cy=\"20\" r=\"0.7\"/><circle cx=\"6\" cy=\"28\" r=\"0.4\"/>\
<circle cx=\"32\" cy=\"32\" r=\"0.5\"/><circle cx=\"11\" cy=\"38\" r=\"0.7\"/>\
</svg>');
background-size: 160px 160px, 100% 100%;
}
/* perforation holes (left edge) */
.container::before {
content: '';
position: absolute;
top: 26px;
bottom: 26px;
left: 30px;
width: 9px;
background-image: radial-gradient(
circle at center,
#d0c6b7 0px,
#d0c6b7 2px,
transparent 3px
);
background-size: 9px 28px;
background-repeat: repeat-y;
pointer-events: none;
}
/* curled top-right corner */
.container::after {
content: '';
position: absolute;
top: 0;
right: 0;
width: 110px;
height: 110px;
background: linear-gradient(
135deg,
rgba(0, 0, 0, 0.08) 0%,
rgba(0, 0, 0, 0) 42%
),
linear-gradient(
135deg,
#fffefa 0%,
#fffefa 50%,
rgba(255, 255, 255, 0) 51%
);
background-size: 100% 100%;
border-bottom-left-radius: 12px;
transform: translate(1px, -1px);
pointer-events: none;
}
.header {
text-align: center;
margin-bottom: 34px;
padding-bottom: 18px;
border-bottom: 1px solid #f2ede3;
}
h1 {
font-family: 'Libre Baskerville', serif;
margin: 0;
font-size: 30px;
letter-spacing: 0.5px;
}
.subtitle {
font-family: 'PT Mono', monospace;
font-size: 14px;
letter-spacing: 1px;
color: #666;
margin-top: 6px;
}
#content {
min-height: 520px;
font-size: 18px;
position: relative;
padding: 10px 0 10px 26px; /* leave room for “margin” lines */
overflow-wrap: break-word;
hyphens: auto;
}
/* faint horizontal ruled lines (lined paper) */
#content::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2.65em,
rgba(201, 190, 170, 0.18) 2.65em,
rgba(201, 190, 170, 0.18) 2.7em
);
pointer-events: none;
z-index: 1;
}
/* bring actual text above the ruled lines */
#content * {
position: relative;
z-index: 2;
}
/* placeholder */
.placeholder {
color: #888;
font-style: italic;
text-align: center;
padding: 110px 20px;
user-select: none;
}
/* markdown tweaks */
blockquote {
border-left: 4px solid #ccbfae;
margin: 20px 0;
padding: 15px 26px;
background: #fbf8f1;
font-style: italic;
}
code {
font-family: 'PT Mono', monospace;
background: #f2efe8;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
pre {
background: #f4f2ec;
padding: 16px 20px;
border: 1px solid #e6e0d2;
border-radius: 6px;
overflow-x: auto;
font-family: 'PT Mono', monospace;
}
/* ordered-list “a) / b)” style */
ol {
counter-reset: item;
padding-left: 0;
list-style: none;
}
ol > li {
counter-increment: item;
margin: 0.5em 0 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) ')';
}
/* 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 0.25s;
z-index: 2000;
}
.processing.show {
opacity: 0.9;
}
/* small screens */
@media (max-width: 768px) {
.container {
margin: 20px 16px;
padding: 28px;
}
#content {
font-size: 16px;
}
h1 {
font-size: 24px;
}
}
/* print */
@media print {
body {
background: #fff;
}
.container {
box-shadow: none !important;
border: none;
}
.header,
.processing,
.instructions {
display: none;
}
}
</style>
</head>
<body>
<div class="container">
<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>
const content = document.getElementById('content');
const processingIndicator = document.querySelector('.processing');
/* ------------- helper: processing badge ---------------- */
function showProcessing() {
processingIndicator.classList.add('show');
}
function hideProcessing() {
setTimeout(() => processingIndicator.classList.remove('show'), 300);
}
/* ------------- main “render” pipeline ------------------ */
function processContent(text) {
showProcessing();
/* protect LaTeX so marked.js doesn’t mangle it */
const store = [];
const PLACE = (i) => `%%LATEX_${i}%%`;
let idx = 0;
function keep(m) {
store.push(m);
return PLACE(idx++);
}
/* Display: \[...\], $$...$$ then Inline: \(...\), $...$ */
text = text
.replace(/\\\[[\s\S]*?\\\]/g, keep)
.replace(/\$\$[\s\S]*?\$\$/g, keep)
.replace(/\\\([\s\S]*?\\\)/g, keep)
/* single $…$ but NOT $$…$$ */
.replace(/\$([^\$\n]+?)\$/g, keep);
/* markdown → HTML */
let html = marked.parse(text);
/* restore LaTeX */
store.forEach((latex, i) => {
html = html.replaceAll(PLACE(i), latex);
});
content.innerHTML = html;
/* trigger MathJax */
if (window.MathJax?.typesetPromise) {
MathJax.typesetPromise([content]).then(hideProcessing).catch(e => {
console.error('MathJax error:', e);
hideProcessing();
});
} else {
hideProcessing();
}
}
/* ------------- paste listeners ------------------------- */
document.addEventListener('paste', (e) => {
e.preventDefault();
const text = e.clipboardData.getData('text/plain');
if (text.trim()) processContent(text);
});
/* subtle “rubber-band” hint if user clicks before any content */
content.addEventListener('click', () => {
const ph = content.querySelector('.placeholder');
if (ph) {
ph.style.transform = 'scale(0.97)';
ph.style.transition = 'transform 0.12s';
setTimeout(() => (ph.style.transform = 'scale(1)'), 120);
}
});
/* smooth initial fade-in */
document.addEventListener('DOMContentLoaded', () => {
const sheet = document.querySelector('.container');
sheet.style.opacity = '0';
setTimeout(() => {
sheet.style.transition = 'opacity 0.6s ease';
sheet.style.opacity = '1';
}, 80);
});
</script>
</body>
</html>