initial commit
Hunter
Monday, July 7th, 2025 at 2:39 PM
1 +<!DOCTYPE html>
2 +<html lang="en">
3 +<head>
4 + <meta charset="UTF-8">
5 + <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 + <title>HTML Laboratory</title>
7 + <style>
8 + :root {
9 + --bg-color: #2d2d2d;
10 + --text-color: #f8f8f2;
11 + --editor-bg: #1e1e1e;
12 + --border-color: #222;
13 + --header-bg: #333;
14 + --header-text: #ccc;
15 + --preview-bg: #2d2d2d;
16 + }
17 +
18 + @media (prefers-color-scheme: light) {
19 + :root {
20 + --bg-color: #f0f0f0;
21 + --text-color: #333;
22 + --editor-bg: #fafafa;
23 + --border-color: #ccc;
24 + --header-bg: #ddd;
25 + --header-text: #666;
26 + --preview-bg: white;
27 + }
28 + }
29 +
30 + * {
31 + margin: 0;
32 + padding: 0;
33 + box-sizing: border-box;
34 + }
35 +
36 + body {
37 + font-family: monospace;
38 + background: var(--bg-color);
39 + color: var(--text-color);
40 + height: 100vh;
41 + display: flex;
42 + overflow: hidden;
43 + }
44 +
45 + .editor-pane {
46 + width: 50%;
47 + height: 100vh;
48 + background: var(--preview-bg);
49 + border-right: 1px solid var(--border-color);
50 + position: relative;
51 + }
52 +
53 + .preview-pane {
54 + width: 50%;
55 + height: 100vh;
56 + background: var(--preview-bg);
57 + position: relative;
58 + }
59 +
60 + #editor {
61 + width: 100%;
62 + height: 100%;
63 + border: none;
64 + outline: none;
65 + font-family: monospace;
66 + font-size: 21px;
67 + padding: 15px;
68 + resize: none;
69 + overflow: auto;
70 + background: var(--editor-bg);
71 + color: var(--text-color);
72 + spellcheck: false;
73 + tab-size: 4;
74 + }
75 +
76 + #preview {
77 + width: 100%;
78 + height: 100%;
79 + border: none;
80 + background: white;
81 + overflow: auto;
82 + }
83 +
84 + #editor {
85 + padding-top: 15px;
86 + }
87 +
88 + #preview {
89 + padding-top: 0;
90 + }
91 +
92 + /* Hide scrollbars until needed */
93 + ::-webkit-scrollbar {
94 + width: 12px;
95 + }
96 +
97 + ::-webkit-scrollbar-track {
98 + background: var(--editor-bg);
99 + }
100 +
101 + ::-webkit-scrollbar-thumb {
102 + background: var(--border-color);
103 + border-radius: 6px;
104 + }
105 +
106 + ::-webkit-scrollbar-thumb:hover {
107 + background: var(--header-text);
108 + }
109 + </style>
110 +</head>
111 +<body>
112 + <div class="editor-pane">
113 + <textarea id="editor" placeholder="Type your HTML, CSS, and JavaScript here..." spellcheck="false"></textarea>
114 + </div>
115 +
116 + <div class="preview-pane">
117 + <iframe id="preview" sandbox="allow-scripts"></iframe>
118 + </div>
119 +
120 + <script>
121 + let updateTimer;
122 + const editor = document.getElementById('editor');
123 + const preview = document.getElementById('preview');
124 + const storageKey = 'html-lab-content';
125 +
126 + function updatePreview() {
127 + const code = editor.value;
128 + const blob = new Blob([code], { type: 'text/html' });
129 + const url = URL.createObjectURL(blob);
130 + preview.src = url;
131 +
132 + // Clean up the previous URL to prevent memory leaks
133 + setTimeout(() => URL.revokeObjectURL(url), 1000);
134 + }
135 +
136 + function saveToStorage() {
137 + try {
138 + localStorage.setItem(storageKey, editor.value);
139 + } catch (e) {
140 + // Handle localStorage errors silently
141 + console.warn('Could not save to localStorage:', e);
142 + }
143 + }
144 +
145 + function loadFromStorage() {
146 + try {
147 + const saved = localStorage.getItem(storageKey);
148 + if (saved !== null) {
149 + editor.value = saved;
150 + return true;
151 + }
152 + } catch (e) {
153 + // Handle localStorage errors silently
154 + console.warn('Could not load from localStorage:', e);
155 + }
156 + return false;
157 + }
158 +
159 +
160 + // Handle Tab key to insert tab character
161 + editor.addEventListener('keydown', function(e) {
162 + if (e.key === 'Tab') {
163 + e.preventDefault();
164 +
165 + const start = this.selectionStart;
166 + const end = this.selectionEnd;
167 +
168 + // Insert tab character at cursor position
169 + this.value = this.value.substring(0, start) + '\t' + this.value.substring(end);
170 +
171 + // Move cursor to after the inserted tab
172 + this.selectionStart = this.selectionEnd = start + 1;
173 +
174 + // Trigger input event to update preview
175 + this.dispatchEvent(new Event('input'));
176 + }
177 + });
178 +
179 + // Update preview after 0.5 seconds of no typing
180 + editor.addEventListener('input', function() {
181 + clearTimeout(updateTimer);
182 + updateTimer = setTimeout(updatePreview, 500);
183 +
184 + // Save to localStorage on every change
185 + saveToStorage();
186 + });
187 +
188 + // Save functionality with Cmd+S
189 + document.addEventListener('keydown', function(e) {
190 + if ((e.metaKey || e.ctrlKey) && e.key === 's') {
191 + e.preventDefault();
192 + saveFile();
193 + }
194 + });
195 +
196 + function saveFile() {
197 + const code = editor.value;
198 + const blob = new Blob([code], { type: 'text/html' });
199 + const url = URL.createObjectURL(blob);
200 +
201 + const a = document.createElement('a');
202 + a.href = url;
203 + a.download = 'index.html';
204 + a.click();
205 +
206 + URL.revokeObjectURL(url);
207 + }
208 +
209 + // Initialize the editor content and render
210 + function initialize() {
211 + // Always try to load from localStorage regardless of refresh type
212 + loadFromStorage();
213 +
214 + // Focus the editor
215 + editor.focus();
216 +
217 + // Initial render
218 + updatePreview();
219 + }
220 +
221 + // Initialize when page loads
222 + initialize();
223 + </script>
224 +</body>
225 +</html>