Repository navigation
Expand file tree
/
Copy pathhtml-template.html
More file actions
107 lines (103 loc) · 4.93 KB
/
Copy pathhtml-template.html
File metadata and controls
107 lines (103 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HTML Template Engine | tools.eliana.lol</title>
<link rel="stylesheet" href="../global.css" />
<link rel="icon" type="image/x-icon" href="../favicon.svg" />
<style>
.tool-layout { display: flex; flex-direction: column; gap: 20px; }
.action-bar { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; }
label { display: block; font-size: 0.7rem; text-transform: uppercase; font-weight: bold; color: var(--puny); margin-bottom: 5px; }
.three-col { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .three-col { grid-template-columns: 1fr; } }
#error { color: var(--accent); font-size: 0.8rem; min-height: 1.2rem; }
#preview-frame { border: 1px solid var(--puny); width: 100%; height: 300px; background: #fff; }
</style>
</head>
<body>
<nav>
<div class="left"><a href="../index.html">home</a> | <a href="../extra.html">extra</a> | <a href="../credits.html">credits</a> | <a href="../changelog.html">logs</a></div>
<div class="right"><button id="theme-toggle">[theme]</button> | <a href="https://eliana.lol">site</a></div>
</nav>
<main>
<h1>HTML Template Engine</h1>
<p class="puny">Write a Handlebars template, provide JSON data, get rendered HTML. Download the result.</p>
<div class="tool-layout">
<div class="three-col">
<div>
<label>Handlebars template</label>
<textarea id="tmpl" rows="16" oninput="render()"></textarea>
</div>
<div>
<label>JSON data</label>
<textarea id="data" rows="16" oninput="render()"></textarea>
</div>
<div>
<label>Rendered HTML</label>
<textarea id="output" rows="16" readonly></textarea>
</div>
</div>
<div id="error"></div>
<div>
<label>Preview</label>
<iframe id="preview-frame" sandbox="allow-same-origin"></iframe>
</div>
<div class="action-bar">
<button onclick="loadSample()">Sample</button>
<button onclick="copyOutput()">Copy HTML</button>
<button onclick="downloadHTML()">Download HTML</button>
<button onclick="clearAll()">Clear</button>
</div>
</div>
</main>
<footer style="margin-top:4rem;text-align:center" class="puny">© 2026 eliana.lol • <a href="../credits.html">credits</a></footer>
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.7.8/dist/handlebars.min.js"></script>
<script>
const htmlEl = document.documentElement;
const toggle = document.getElementById('theme-toggle');
const saved = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
htmlEl.setAttribute('data-theme', saved);
if (toggle) toggle.innerText = saved === 'dark' ? '[light]' : '[dark]';
if (toggle) toggle.onclick = () => { const now = htmlEl.getAttribute('data-theme'); const next = now==='dark'?'light':'dark'; htmlEl.setAttribute('data-theme',next); localStorage.setItem('theme',next); toggle.innerText=next==='dark'?'[light]':'[dark]'; };
function render() {
const tmplSrc = document.getElementById('tmpl').value;
const dataSrc = document.getElementById('data').value;
const err = document.getElementById('error');
try {
const data = JSON.parse(dataSrc);
const compiled = Handlebars.compile(tmplSrc);
const html = compiled(data);
document.getElementById('output').value = html;
document.getElementById('preview-frame').srcdoc = html;
err.textContent = '';
} catch(e) {
err.textContent = e.message;
}
}
function loadSample() {
document.getElementById('tmpl').value = `<h1>{{title}}</h1>\n<p>By {{author}}</p>\n<ul>\n{{#each items}}\n <li><strong>{{name}}</strong> — {{desc}}</li>\n{{/each}}\n</ul>\n{{#if footer}}<footer>{{footer}}</footer>{{/if}}`;
document.getElementById('data').value = JSON.stringify({
title: "My Tool Collection",
author: "eliana",
items: [
{ name: "CSV Parser", desc: "Parse CSV to JSON" },
{ name: "Diff Viewer", desc: "Compare text changes" },
{ name: "QR Generator", desc: "Make QR codes" }
],
footer: "tools.eliana.lol"
}, null, 2);
render();
}
function copyOutput() { navigator.clipboard.writeText(document.getElementById('output').value); }
function downloadHTML() {
const a = document.createElement('a');
a.href = 'data:text/html;charset=utf-8,'+encodeURIComponent(document.getElementById('output').value);
a.download = 'rendered.html'; a.click();
}
function clearAll() { ['tmpl','data','output'].forEach(id => document.getElementById(id).value=''); document.getElementById('preview-frame').srcdoc=''; document.getElementById('error').textContent=''; }
loadSample();
</script>
</body>
</html>