airabbitX commited on
Commit
0547596
·
verified ·
1 Parent(s): a8042f8

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +847 -18
index.html CHANGED
@@ -1,19 +1,848 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>HTML Pro Editor</title>
7
+
8
+ <!-- Host-page CSS (the iframe can’t touch these rules) -->
9
+ <style>
10
+ *{box-sizing:border-box;margin:0;padding:0}
11
+ body{
12
+ font-family:system-ui,sans-serif;
13
+ background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);
14
+ min-height:100vh;display:flex;flex-direction:column;
15
+ }
16
+ /* MODIFIED TOOLBAR STYLES */
17
+ .toolbar{
18
+ padding:12px 16px;
19
+ background:#fff;
20
+ box-shadow:0 2px 8px rgba(0,0,0,.15);
21
+ display:flex;
22
+ justify-content:space-between;
23
+ align-items:flex-start;
24
+ gap:15px;
25
+ flex-wrap: wrap;
26
+ position: fixed;
27
+ top: 0;
28
+ left: 0;
29
+ right: 0;
30
+ width: 100%;
31
+ z-index: 100;
32
+ transition: top 0.3s ease-out;
33
+ }
34
+ .toolbar.hidden { top: -150px; }
35
+ .toolbar.manual-hidden { top: -150px; }
36
+ .toolbar-branding {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 8px;
40
+ font-size: 1.2em;
41
+ font-weight: bold;
42
+ color: #333;
43
+ padding-bottom: 5px;
44
+ }
45
+ .toolbar-icon { font-size: 1.5em; line-height: 1; }
46
+ .toolbar button,
47
+ .toolbar select,
48
+ .toolbar input[type="color"]{
49
+ font:14px/1.2 system-ui,sans-serif;
50
+ padding:6px 10px;
51
+ border:1px solid #ccc;
52
+ border-radius:4px;
53
+ background:#fdfdfd;
54
+ cursor:pointer;
55
+ height: 34px;
56
+ }
57
+ .toolbar input[type="color"] {
58
+ width: 40px;
59
+ padding: 0;
60
+ }
61
+ .toolbar-left-section {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 8px;
65
+ flex-grow: 1;
66
+ }
67
+ .toolbar-row {
68
+ display: flex;
69
+ flex-wrap: wrap;
70
+ gap: 6px;
71
+ align-items: center;
72
+ }
73
+ #imageBtn {background:#fffbea}
74
+ #clipBtn {background:#eaf0ff}
75
+ #resetBtn {background:#ffecec}
76
+ #helpBtn {background:#ecf8ff}
77
+ #selectBtn {background:#e8f4fd}
78
+ #deleteSelectedBtn {background:#ffecec}
79
+ .element-selector-overlay {
80
+ position: fixed;
81
+ background: rgba(0, 100, 200, 0.2);
82
+ border: 2px solid #0066cc;
83
+ pointer-events: none;
84
+ z-index: 999999;
85
+ box-sizing: border-box;
86
+ }
87
+ .selected-element {
88
+ outline: 4px solid #ff6b6b !important;
89
+ outline-offset: 2px !important;
90
+ background: rgba(255, 107, 107, 0.1) !important;
91
+ box-shadow: 0 0 0 2px rgba(255, 107, 107, 0.3) !important;
92
+ }
93
+ .toolbar-right-side {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 8px;
97
+ align-items: flex-end;
98
+ flex-shrink: 0;
99
+ }
100
+ .views{
101
+ display:flex;
102
+ gap:4px;
103
+ }
104
+ .views button{padding:4px 8px;font-size:12px}
105
+ .floating-toolbar-toggle {
106
+ position: fixed;
107
+ top: 120px;
108
+ right: 0;
109
+ z-index: 101;
110
+ padding: 8px 6px;
111
+ border: none;
112
+ border-radius: 6px 0 0 6px;
113
+ background: rgba(255, 255, 255, 0.95);
114
+ cursor: pointer;
115
+ font-size: 12px;
116
+ writing-mode: vertical-rl;
117
+ text-orientation: mixed;
118
+ box-shadow: -2px 0 8px rgba(0,0,0,.15);
119
+ transition: all 0.3s ease;
120
+ border: 1px solid rgba(0,0,0,0.1);
121
+ border-right: none;
122
+ transform: translateX(0);
123
+ }
124
+ .floating-toolbar-toggle:hover {
125
+ background: #f5f5f5;
126
+ transform: translateX(-5px);
127
+ box-shadow: -4px 0 12px rgba(0,0,0,.2);
128
+ }
129
+ .floating-toolbar-toggle:active {
130
+ transform: translateX(-2px);
131
+ }
132
+ .main{
133
+ flex:1;display:flex;flex-direction:column;margin:12px;
134
+ background:rgba(255,255,255,.94);border-radius:12px;overflow:hidden;
135
+ box-shadow:0 10px 40px rgba(0,0,0,.1);
136
+ margin-top: 0;
137
+ transition: margin-top 0.3s ease-out;
138
+ }
139
+ #editorFrame{flex:1;width:100%;border:none}
140
+ #codeView{
141
+ flex:1;width:100%;border:none;padding:16px;resize:none;outline:none;
142
+ font:13px/1.4 Monaco,Menlo,monospace;display:none
143
+ }
144
+ #ctxMenu{
145
+ position:absolute;display:none;flex-direction:column;
146
+ background:#fff;border:1px solid #ccc;border-radius:4px;
147
+ box-shadow:0 4px 16px rgba(0,0,0,.15);z-index:3;min-width:140px;
148
+ }
149
+ #ctxMenu button{all:unset;padding:8px 14px;font:13px system-ui;cursor:pointer;white-space:nowrap;}
150
+ #ctxMenu button:hover{background:#f0f2ff}
151
+ #helpModal{
152
+ position:fixed;inset:0;background:rgba(0,0,0,.55);
153
+ display:none;align-items:center;justify-content:center;z-index:4;
154
+ }
155
+ #helpModal .panel{
156
+ background:#fff;border-radius:8px;max-width:600px;width:90%;padding:24px;
157
+ box-shadow:0 6px 24px rgba(0,0,0,.25);overflow-y:auto;max-height:80vh;
158
+ }
159
+ #helpModal h2{margin-bottom:12px;font-size:20px}
160
+ #helpModal ul{margin-left:18px;margin-top:6px}
161
+ #helpModal button{margin-top:16px;padding:6px 14px;font:14px system-ui;border:1px solid #ccc;border-radius:4px;cursor:pointer}
162
+ </style>
163
+ </head>
164
+ <body>
165
+
166
+ <!-- ────────── TOOLBAR ────────── -->
167
+ <div class="toolbar">
168
+ <div class="toolbar-left-section">
169
+ <div class="toolbar-row">
170
+ <select onchange="cmd('formatBlock',this.value)">
171
+ <option value="">Format</option><option value="<h1>">H1</option>
172
+ <option value="<h2>">H2</option><option value="<h3>">H3</option>
173
+ <option value="<p>">Paragraph</option><option value="blockquote">Quote</option>
174
+ <option value="<pre>">Code</option>
175
+ </select>
176
+
177
+ <button onclick="cmd('bold')"><b>B</b></button>
178
+ <button onclick="cmd('italic')"><i>I</i></button>
179
+ <button onclick="cmd('underline')"><u>U</u></button>
180
+ <button onclick="cmd('strikeThrough')"><s>S</s></button>
181
+
182
+ <button onclick="cmd('insertUnorderedList')">• List</button>
183
+ <button onclick="cmd('insertOrderedList')">1. List</button>
184
+
185
+ <!-- Color Controls Group -->
186
+ <span style="margin-left: 8px; margin-right: 8px; border-left: 1px solid #ddd; padding-left: 8px;">
187
+ <input type="color" title="Document text color" oninput="setDocumentTextColor(this.value)">
188
+ </span>
189
+
190
+ <!-- File Controls -->
191
+ <input type="file" id="htmlFileInput" accept=".html,.htm" style="display:none" onchange="loadHTMLFile(this)">
192
+ <button onclick="document.getElementById('htmlFileInput').click()">📂 Upload HTML</button>
193
+ </div>
194
+
195
+ <div class="toolbar-row">
196
+ <button onclick="insertLink()">🔗 Link</button>
197
+ <button onclick="insertImage()">🖼️ Image URL</button>
198
+ <button id="imageBtn" onclick="replaceSelectionWithImage()">Add Image</button>
199
+ <button onclick="insertTable()">📊 Table</button>
200
+ <button onclick="cmd('insertHorizontalRule')">─ Rule</button>
201
+ <button id="selectBtn" onclick="activateElementSelector()">🎯 Select Element</button>
202
+ <button id="deleteSelectedBtn" onclick="deleteSelectedElement()" style="display:none">🗑️ Delete Selected</button>
203
+
204
+ <button onclick="undo()">↶ Undo</button>
205
+ <button onclick="redo()">↷ Redo</button>
206
+
207
+ <button onclick="downloadHTML()">💾 Save</button>
208
+ <button onclick="exportPDF()">📄 PDF</button>
209
+ <button id="clipBtn" onclick="importClipboard()">Import Clipboard</button>
210
+ <button id="helpBtn" onclick="toggleHelp()">Help</button>
211
+ </div>
212
+ </div>
213
+
214
+ <!-- NEW: Container for branding and views on the right -->
215
+ <div class="toolbar-right-side">
216
+ <!-- BRANDING SECTION -->
217
+ <div class="toolbar-branding">
218
+ <span class="toolbar-icon">✍️</span>
219
+ <span class="toolbar-name">Tiny HTML Editor</span>
220
+ </div>
221
+ <!---------------------->
222
+ <div class="views">
223
+ <button id="vDesign" onclick="view('design')" style="background:#667eea;color:#fff;">Design</button>
224
+ <button id="vCode" onclick="view('code')">Code</button>
225
+ <button id="vSplit" onclick="view('split')">Split</button>
226
+ </div>
227
+ </div>
228
+ </div>
229
+
230
+ <!-- Slide-out Tab Toggle Button -->
231
+ <button id="toggleToolbarBtn" onclick="toggleToolbar()" class="floating-toolbar-toggle">▲ Hide</button>
232
+
233
+ <!-- Main editor area -->
234
+ <div class="main">
235
+ <iframe id="editorFrame" sandbox="allow-scripts allow-same-origin"></iframe>
236
+ <textarea id="codeView"></textarea>
237
+ </div>
238
+
239
+ <!-- Repeatable context menu -->
240
+ <div id="ctxMenu">
241
+ <button onclick="dupRepeat()">Duplicate</button>
242
+ <button onclick="delRepeat()">Delete</button>
243
+ </div>
244
+
245
+ <!-- Help modal -->
246
+ <div id="helpModal">
247
+ <div class="panel">
248
+ <h2>Editor Help</h2>
249
+ <ul>
250
+ <li><strong>Add Image</strong> – click “Add Image” to upload; no text selection needed.</li>
251
+ <li><strong>Select Element</strong> – click "Select Element", hover and click to select, then delete.</li>
252
+ <li><strong>Resize & Move</strong> – drag image corners to resize or drag to move.</li>
253
+ <li><strong>Import Clipboard</strong> – pastes raw HTML from your clipboard into the editor.</li>
254
+ <li><strong>Undo / Redo</strong> – toolbar buttons or <kbd>Ctrl/Cmd + Z/Y</kbd>.</li>
255
+ <li><strong>Views</strong> – switch between Design, Code, or Split view.</li>
256
+ </ul>
257
+ <button onclick="toggleHelp()">Close</button>
258
+ </div>
259
+ </div>
260
+
261
+
262
+
263
+ <!-- ────────── SCRIPT ────────── -->
264
+ <script>
265
+ const frame = document.getElementById('editorFrame');
266
+ const codeBox = document.getElementById('codeView');
267
+ const ctxMenu = document.getElementById('ctxMenu');
268
+ const helpModal=document.getElementById('helpModal');
269
+ const toolbar = document.querySelector('.toolbar');
270
+ const mainContent = document.querySelector('.main');
271
+ const toggleToolbarBtn = document.getElementById('toggleToolbarBtn');
272
+ let currentRepeatable=null;
273
+
274
+ /* UNDO / REDO STACK */
275
+ const undoStack=[], redoStack=[], MAX_HIST=100;
276
+ function pushState(){
277
+ const html=frame.contentDocument.body.innerHTML;
278
+ if(!undoStack.length||undoStack[undoStack.length-1]!==html){
279
+ undoStack.push(html);
280
+ if(undoStack.length>MAX_HIST)undoStack.shift();
281
+ redoStack.length=0;
282
+ }
283
+ }
284
+ function restore(html){loadHTMLWithScripts(html);scanPlaceholders();sync(false);}
285
+ function undo(){if(undoStack.length>1){redoStack.push(undoStack.pop());restore(undoStack[undoStack.length-1]);}}
286
+ function redo(){if(redoStack.length){const h=redoStack.pop();undoStack.push(h);restore(h);}}
287
+
288
+ /* INITIAL DOC */
289
+ const initialHTML=`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><style>
290
+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
291
+ padding:20px;line-height:1.6;min-height:100vh}
292
+ table{border-collapse:collapse;width:100%;margin:15px 0}
293
+ td,th{border:1px solid #ddd;padding:8px;text-align:left}
294
+ th{background:#f8f9fa;font-weight:600}
295
+ blockquote{border-left:4px solid #667eea;padding:10px 20px;background:#f8f9fa}
296
+ img{max-width:100%;height:auto;margin:10px 0}
297
+ .placeholder-image{border:2px dashed #667eea;opacity:.8;cursor:pointer;position:relative}
298
+ .placeholder-image::after{content:'📷 Click to upload';position:absolute;top:50%;left:50%;
299
+ transform:translate(-50%,-50%);background:rgba(102,126,234,.9);color:#fff;
300
+ padding:6px 10px;border-radius:4px;font-size:12px;font-weight:600;pointer-events:none}
301
+ [repeatable]{outline:1px dashed #999}
302
+ </style></head><body contenteditable="true">
303
+ <h1>John Doe</h1>
304
+ <p><b>Web Developer</b> | Passionate about building interactive experiences</p>
305
+
306
+ <h2>Contact</h2>
307
+ <ul repeatable>
308
+ <li>Email: <a href="mailto:[email protected]">[email protected]</a></li>
309
+ <li>Phone: +1 (123) 456-7890</li>
310
+ <li>LinkedIn: <a href="#">linkedin.com/in/johndoe</a></li>
311
+ <li>Portfolio: <a href="#">johndoe.dev</a></li>
312
+ </ul>
313
+
314
+ <h2>Summary</h2>
315
+ <p>Enthusiastic and results-driven Web Developer with 3+ years of experience in creating responsive and user-friendly websites. Proficient in HTML, CSS, JavaScript, and modern front-end frameworks.</p>
316
+
317
+ <h2>Experience</h2>
318
+ <div repeatable style="padding:10px; border:1px solid #e0e0e0; margin:10px 0; border-radius:4px; background-color:#f9f9f9;">
319
+ <h3>Junior Web Developer</h3>
320
+ <p><i>Tech Solutions Inc.</i> | Jan 2021 - Present</p>
321
+ <ul>
322
+ <li>Developed and maintained client websites using HTML, CSS, and JavaScript.</li>
323
+ <li>Collaborated with design teams to translate mockups into functional web pages.</li>
324
+ <li>Optimized web applications for maximum speed and scalability.</li>
325
+ </ul>
326
+ </div>
327
+
328
+ <div repeatable style="padding:10px; border:1px solid #e0e0e0; margin:10px 0; border-radius:4px; background-color:#f9f9f9;">
329
+ <h3>Intern Developer</h3>
330
+ <p><i>Startup Innovations</i> | Summer 2020</p>
331
+ <ul>
332
+ <li>Assisted senior developers in building new features for a SaaS platform.</li>
333
+ <li>Contributed to front-end development and bug fixing.</li>
334
+ </ul>
335
+ </div>
336
+
337
+ <h2>Education</h2>
338
+ <p><b>Bachelor of Science in Computer Science</b> | University of Tech | 2017 - 2020</p>
339
+
340
+ <h2>Skills</h2>
341
+ <ul repeatable>
342
+ <li><b>Languages:</b> HTML, CSS, JavaScript (ES6+), Python</li>
343
+ <li><b>Frameworks:</b> React, Vue.js, Node.js</li>
344
+ <li><b>Tools:</b> Git, Webpack, Babel, npm</li>
345
+ </ul>
346
+
347
+ <img src="https://via.placeholder.com/200x150/667eea/ffffff?text=Your+Photo" alt="Placeholder for user photo" class="placeholder-image">
348
+ </body></html>`;
349
+
350
+ frame.srcdoc=initialHTML;
351
+ frame.addEventListener('load',setupFrame);
352
+
353
+ /* execCommand wrapper */
354
+ function cmd(c,v=null){
355
+ const d=frame.contentDocument;if(!d)return;
356
+ d.execCommand(c,false,v);frame.contentWindow.focus();
357
+ scanPlaceholders();sync();pushState();
358
+ }
359
+
360
+ /* INSERT FUNCTIONS */
361
+ function insertLink(){const u=prompt('Enter URL:');if(u)cmd('createLink',u);}
362
+ function insertImage(){
363
+ const u=prompt('Image URL (leave blank to upload):');if(u===null)return;
364
+ u===''?upload(data=>cmd('insertImage',data)):cmd('insertImage',u);
365
+ }
366
+ function upload(cb){
367
+ const inp=document.createElement('input');inp.type='file';inp.accept='image/*';
368
+ inp.onchange=e=>{
369
+ const f=e.target.files[0];if(f){const r=new FileReader();r.onload=ev=>cb(ev.target.result);r.readAsDataURL(f);}
370
+ };inp.click();
371
+ }
372
+ function insertTable(){
373
+ const r=+prompt('Rows:',3),c=+prompt('Cols:',3);
374
+ if(r&&c){
375
+ let h='<table><tbody>';
376
+ for(let i=0;i<r;i++){h+='<tr>';for(let j=0;j<c;j++)h+=i?'<td>Cell</td>':'<th>Header</th>';h+='</tr>'; }
377
+ h+='</tbody></table>';cmd('insertHTML',h);
378
+ }
379
+ }
380
+
381
+ /* PLACEHOLDER LOGIC */
382
+ function isPH(src){return /placeholder\.com|via\.placeholder\.com|placehold\.it|picsum\.photos|dummyimage\.com|fakeimg\.pl|placeholder\.pics/i.test(src);}
383
+ function scanPlaceholders(){
384
+ frame.contentDocument.querySelectorAll('img').forEach(img=>{
385
+ if(isPH(img.src)||img.classList.contains('placeholder-image')){
386
+ img.classList.add('placeholder-image');
387
+ img.onclick=()=>replacePlaceholder(img);
388
+ }
389
+ });
390
+ }
391
+ function replacePlaceholder(img){
392
+ upload(data=>{
393
+ img.src=data;img.classList.remove('placeholder-image');img.onclick=null;
394
+ sync();pushState();
395
+ });
396
+ }
397
+
398
+ /* ADD IMAGE (replace selection or append if none) */
399
+ function replaceSelectionWithImage(){
400
+ const d = frame.contentDocument, sel = d.getSelection();
401
+ upload(data => {
402
+ // 1) Create a wrapper that’s resizable
403
+ const wrapper = d.createElement('div');
404
+ wrapper.style.resize = 'both';
405
+ wrapper.style.overflow = 'auto';
406
+ wrapper.style.display = 'inline-block';
407
+ wrapper.style.border = '1px dashed #666'; // optional visual cue
408
+
409
+ // 2) Create the image inside it
410
+ const img = d.createElement('img');
411
+ img.src = data;
412
+ img.style.display = 'block';
413
+ img.style.width = '100%'; // make it fill the wrapper
414
+ img.style.height = 'auto';
415
+
416
+ wrapper.appendChild(img);
417
+
418
+ // 3) Insert wrapper (or append if no selection)
419
+ if (sel.rangeCount) {
420
+ const r = sel.getRangeAt(0);
421
+ r.deleteContents();
422
+ r.insertNode(wrapper);
423
+ } else {
424
+ d.body.appendChild(wrapper);
425
+ }
426
+
427
+ sel.removeAllRanges();
428
+ scanPlaceholders();
429
+ sync();
430
+ pushState();
431
+ });
432
+ }
433
+
434
+ /* CLIPBOARD IMPORT */
435
+ let lastImported='';
436
+ async function importClipboard(silent = false){
437
+ try{
438
+ let rawHtml = await navigator.clipboard.readText();
439
+ let cleanedHtml = rawHtml;
440
+ if (cleanedHtml) {
441
+ if (cleanedHtml.startsWith("```html")) {
442
+ cleanedHtml = cleanedHtml.substring(7);
443
+ }
444
+ if (cleanedHtml.endsWith("```")) {
445
+ cleanedHtml = cleanedHtml.substring(0, cleanedHtml.length - 3);
446
+ }
447
+ cleanedHtml = cleanedHtml.trim();
448
+ }
449
+ const html = cleanedHtml;
450
+ if(html && html !== lastImported && /<[a-z]/i.test(html)){
451
+ loadHTMLWithScripts(html);
452
+ lastImported=html;
453
+ scanPlaceholders();
454
+ sync();
455
+ pushState();
456
+ if (!silent) { alert('Clipboard content imported successfully!'); }
457
+ } else if (!silent) {
458
+ if (rawHtml && (!html || !/<[a-z]/i.test(html))) {
459
+ alert('Clipboard content was not valid HTML after cleaning, or became empty.');
460
+ } else if (!rawHtml) {
461
+ alert('Clipboard is empty.');
462
+ } else {
463
+ alert('Clipboard does not contain valid HTML or is unchanged from last import.');
464
+ }
465
+ }
466
+ }catch(err){
467
+ if (!silent) {
468
+ alert('Clipboard access denied or content unavailable. Please ensure you have granted permission.');
469
+ console.error("Clipboard read error:", err);
470
+ }
471
+ }
472
+ }
473
+ function handlePaste(e){
474
+ const html=e.clipboardData.getData('text/html');
475
+ if(html){e.preventDefault();cmd('insertHTML',html);}
476
+ }
477
+
478
+ /* CODE SYNC */
479
+ function tidy(h){return h.replace(/></g,'>\n<').trim();}
480
+ function sync(updateCode=true){
481
+ const html=frame.contentDocument.body.innerHTML;
482
+ if(updateCode)codeBox.value=tidy(html);
483
+ }
484
+ codeBox.addEventListener('input',()=>{
485
+ loadHTMLWithScripts(codeBox.value);
486
+ scanPlaceholders();pushState();
487
+ });
488
+
489
+ /* VIEW SWITCH */
490
+ const vBtns={design:vDesign,code:vCode,split:vSplit};
491
+ function view(v){
492
+ Object.values(vBtns).forEach(b=>{b.style.background='';b.style.color='';});
493
+ vBtns[v].style.background='#667eea';vBtns[v].style.color='#fff';
494
+ if(v==='design'){frame.style.display='block';codeBox.style.display='none'}
495
+ else if(v==='code'){sync();frame.style.display='none';codeBox.style.display='block'}
496
+ else {sync();frame.style.display='block';codeBox.style.display='block';frame.style.height='50%';codeBox.style.height='50%'}
497
+ }
498
+
499
+ /* RESET */
500
+ function resetContent(){
501
+ if(confirm('Clear the editor content?')){
502
+ frame.contentDocument.body.innerHTML='<p></p>';
503
+ scanPlaceholders();sync();pushState();
504
+ }
505
+ }
506
+
507
+ /* SAVE & PDF */
508
+ function downloadHTML(){
509
+ const blob=new Blob([`<!DOCTYPE html><html><body>${frame.contentDocument.body.innerHTML}</body></html>`],{type:'text/html'});
510
+ const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='document.html';a.click();URL.revokeObjectURL(a.href);
511
+ }
512
+ function exportPDF(){
513
+ const w=window.open('','_blank','width=800,height=1000');
514
+ w.document.write(`<!DOCTYPE html><html><body>${frame.contentDocument.body.innerHTML}</body></html>`);w.document.close();
515
+ w.onload=()=>setTimeout(()=>w.print(),300);
516
+ }
517
+
518
+ /* REPEATABLE CONTEXT MENU */
519
+ function handleContext(e){
520
+ let n=e.target, d=frame.contentDocument;
521
+ while(n&&n!==d.body&&!n.hasAttribute('repeatable'))n=n.parentElement;
522
+ if(n&&n.hasAttribute('repeatable')){
523
+ e.preventDefault();currentRepeatable=n;
524
+ const r=frame.getBoundingClientRect();
525
+ ctxMenu.style.left=r.left+e.clientX+'px';
526
+ ctxMenu.style.top =r.top +e.clientY+'px';
527
+ ctxMenu.style.display='flex';
528
+ }else hideCtx();
529
+ }
530
+ function dupRepeat(){if(currentRepeatable){currentRepeatable.parentNode.insertBefore(currentRepeatable.cloneNode(true),currentRepeatable.nextSibling);hideCtx();sync();pushState();}}
531
+ function delRepeat(){if(currentRepeatable){currentRepeatable.remove();hideCtx();sync();pushState();}}
532
+ function hideCtx(){ctxMenu.style.display='none';currentRepeatable=null;}
533
+
534
+ /* ELEMENT SELECTOR */
535
+ let elementSelectorMode = false;
536
+ let selectorOverlay = null;
537
+ let selectedElement = null;
538
+
539
+ function activateElementSelector() {
540
+ if (selectedElement && !elementSelectorMode) {
541
+ selectedElement.classList.remove('selected-element');
542
+ selectedElement = null;
543
+ const deleteBtn = document.getElementById('deleteSelectedBtn');
544
+ deleteBtn.style.display = 'none';
545
+ if (selectorOverlay) {
546
+ selectorOverlay.remove();
547
+ selectorOverlay = null;
548
+ }
549
+ const selectBtn = document.getElementById('selectBtn');
550
+ selectBtn.textContent = '🎯 Select Element';
551
+ selectBtn.style.background = '#e8f4fd';
552
+ return;
553
+ }
554
+
555
+ if (elementSelectorMode) {
556
+ deactivateElementSelector();
557
+ return;
558
+ }
559
+
560
+ elementSelectorMode = true;
561
+ const selectBtn = document.getElementById('selectBtn');
562
+ selectBtn.textContent = '❌ Cancel Select';
563
+ selectBtn.style.background = '#ff9999';
564
+
565
+ selectorOverlay = document.createElement('div');
566
+ selectorOverlay.className = 'element-selector-overlay';
567
+ document.body.appendChild(selectorOverlay);
568
+
569
+ frame.contentDocument.body.style.cursor = 'crosshair';
570
+
571
+ frame.contentDocument.addEventListener('mousemove', handleSelectorMouseMove);
572
+ frame.contentDocument.addEventListener('click', handleSelectorClick);
573
+ frame.contentDocument.addEventListener('keydown', handleSelectorKeyDown);
574
+ }
575
+
576
+ function deactivateElementSelector() {
577
+ elementSelectorMode = false;
578
+ const selectBtn = document.getElementById('selectBtn');
579
+ selectBtn.textContent = '🎯 Select Element';
580
+ selectBtn.style.background = '#e8f4fd';
581
+ if (selectorOverlay) {
582
+ selectorOverlay.remove();
583
+ selectorOverlay = null;
584
+ }
585
+ frame.contentDocument.body.style.cursor = 'auto';
586
+ frame.contentDocument.removeEventListener('mousemove', handleSelectorMouseMove);
587
+ frame.contentDocument.removeEventListener('click', handleSelectorClick);
588
+ frame.contentDocument.removeEventListener('keydown', handleSelectorKeyDown);
589
+ }
590
+
591
+ function handleSelectorMouseMove(e) {
592
+ e.preventDefault();
593
+ e.stopPropagation();
594
+ const element = frame.contentDocument.elementFromPoint(e.clientX, e.clientY);
595
+ if (element && element !== frame.contentDocument.body) {
596
+ updateSelectorOverlay(element);
597
+ }
598
+ }
599
+
600
+ function updateSelectorOverlay(element) {
601
+ if (!selectorOverlay) return;
602
+ const rect = element.getBoundingClientRect();
603
+ const frameRect = frame.getBoundingClientRect();
604
+ selectorOverlay.style.top = (frameRect.top + rect.top) + 'px';
605
+ selectorOverlay.style.left = (frameRect.left + rect.left) + 'px';
606
+ selectorOverlay.style.width = rect.width + 'px';
607
+ selectorOverlay.style.height = rect.height + 'px';
608
+ selectorOverlay.style.display = 'block';
609
+ }
610
+
611
+ function handleSelectorClick(e) {
612
+ e.preventDefault();
613
+ e.stopPropagation();
614
+ const element = frame.contentDocument.elementFromPoint(e.clientX, e.clientY);
615
+ if (element && element !== frame.contentDocument.body) {
616
+ if (element.tagName.toLowerCase() === 'html' || element.tagName.toLowerCase() === 'body') {
617
+ alert('Cannot select the body or html element.');
618
+ return;
619
+ }
620
+ if (selectedElement) {
621
+ selectedElement.classList.remove('selected-element');
622
+ }
623
+ selectedElement = element;
624
+ selectedElement.classList.add('selected-element');
625
+ const deleteBtn = document.getElementById('deleteSelectedBtn');
626
+ deleteBtn.style.display = 'inline-block';
627
+ updateSelectorOverlay(selectedElement);
628
+ elementSelectorMode = false;
629
+ const selectBtn = document.getElementById('selectBtn');
630
+ selectBtn.textContent = '🎯 Unselect Element';
631
+ selectBtn.style.background = '#ffdddd';
632
+ frame.contentDocument.body.style.cursor = 'auto';
633
+ frame.contentDocument.removeEventListener('mousemove', handleSelectorMouseMove);
634
+ frame.contentDocument.removeEventListener('click', handleSelectorClick);
635
+ frame.contentDocument.removeEventListener('keydown', handleSelectorKeyDown);
636
+ }
637
+ }
638
+
639
+ function handleSelectorKeyDown(e) {
640
+ if (e.key === 'Escape') {
641
+ deactivateElementSelector();
642
+ }
643
+ }
644
+
645
+ function deleteSelectedElement() {
646
+ if (!selectedElement) {
647
+ alert('No element selected. Please select an element first.');
648
+ return;
649
+ }
650
+ selectedElement.remove();
651
+ selectedElement = null;
652
+ const deleteBtn = document.getElementById('deleteSelectedBtn');
653
+ deleteBtn.style.display = 'none';
654
+ if (selectorOverlay) {
655
+ selectorOverlay.remove();
656
+ selectorOverlay = null;
657
+ }
658
+ const selectBtn = document.getElementById('selectBtn');
659
+ selectBtn.textContent = '🎯 Select Element';
660
+ selectBtn.style.background = '#e8f4fd';
661
+ sync();
662
+ pushState();
663
+ }
664
+
665
+ /* HELP */
666
+ function toggleHelp(){helpModal.style.display=helpModal.style.display==='flex'?'none':'flex';}
667
+
668
+ /* TOOLBAR AUTO-HIDE / TOGGLE */
669
+ let lastScrollY = 0;
670
+ let toolbarInitialHeight = 0;
671
+ window.addEventListener('scroll', () => {
672
+ const currentScrollY = window.scrollY;
673
+ if (toolbar.classList.contains('manual-hidden')) {
674
+ mainContent.style.marginTop = '0px';
675
+ lastScrollY = currentScrollY;
676
+ return;
677
+ }
678
+ if (currentScrollY > lastScrollY && currentScrollY > toolbarInitialHeight) {
679
+ toolbar.classList.add('hidden');
680
+ mainContent.style.marginTop = '0px';
681
+ }
682
+ else if (currentScrollY < lastScrollY || currentScrollY <= toolbarInitialHeight) {
683
+ toolbar.classList.remove('hidden');
684
+ mainContent.style.marginTop = toolbarInitialHeight + 'px';
685
+ }
686
+ lastScrollY = currentScrollY;
687
+ });
688
+
689
+ function toggleToolbar() {
690
+ const isManuallyHidden = toolbar.classList.contains('manual-hidden');
691
+ if (isManuallyHidden) {
692
+ toolbar.classList.remove('manual-hidden', 'hidden');
693
+ mainContent.style.marginTop = toolbarInitialHeight + 'px';
694
+ toggleToolbarBtn.innerHTML = '▲ Hide';
695
+ } else {
696
+ toolbar.classList.add('manual-hidden', 'hidden');
697
+ mainContent.style.marginTop = '0px';
698
+ toggleToolbarBtn.innerHTML = '▼ Show';
699
+ }
700
+ }
701
+
702
+ /* IFRAME SETUP */
703
+ function setupFrame(){
704
+ const d=frame.contentDocument;
705
+ d.designMode = "on";
706
+ d.execCommand('enableObjectResizing', false, true);
707
+ d.execCommand('enableAbsolutePositionEditor', false, true);
708
+ d.body.addEventListener('input',()=>{scanPlaceholders();sync();pushState();});
709
+ d.body.addEventListener('paste',handlePaste);
710
+ d.body.addEventListener('keydown',e=>{
711
+ if (selectedElement && (e.key === 'Delete' || e.key === 'Backspace')) {
712
+ e.preventDefault();
713
+ deleteSelectedElement();
714
+ return;
715
+ }
716
+ if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==='z'){e.preventDefault();e.shiftKey?redo():undo();}
717
+ if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==='y'){e.preventDefault();redo();}
718
+ });
719
+ d.body.addEventListener('contextmenu',handleContext);
720
+ window.addEventListener('click',hideCtx);
721
+ requestAnimationFrame(() => {
722
+ toolbarInitialHeight = toolbar.offsetHeight;
723
+ mainContent.style.marginTop = toolbarInitialHeight + 'px';
724
+ });
725
+ importClipboard(true);
726
+ scanPlaceholders();sync();pushState();
727
+ }
728
+
729
+ /* FUNCTION TO LOAD HTML WITH SCRIPTS ENABLED */
730
+ function loadHTMLWithScripts(html) {
731
+ const doc = frame.contentDocument;
732
+
733
+ // Set the HTML content
734
+ doc.body.innerHTML = html;
735
+
736
+ // Find and execute all script tags
737
+ const scripts = doc.body.querySelectorAll('script');
738
+ scripts.forEach(oldScript => {
739
+ const newScript = doc.createElement('script');
740
+
741
+ // Copy attributes
742
+ Array.from(oldScript.attributes).forEach(attr => {
743
+ newScript.setAttribute(attr.name, attr.value);
744
+ });
745
+
746
+ // Copy content
747
+ newScript.textContent = oldScript.textContent;
748
+
749
+ // Replace old script with new one to trigger execution
750
+ oldScript.parentNode.replaceChild(newScript, oldScript);
751
+ });
752
+ }
753
+
754
+ /* NEW FUNCTIONS FOR HTML UPLOAD AND COLOR CONTROLS */
755
+ function loadHTMLFile(input) {
756
+ const file = input.files[0];
757
+ if (!file) return;
758
+
759
+ const reader = new FileReader();
760
+ reader.onload = function(e) {
761
+ let rawHtml = e.target.result;
762
+ let cleanedHtml = rawHtml;
763
+
764
+ // Apply the same cleaning logic as clipboard import
765
+ if (cleanedHtml) {
766
+ if (cleanedHtml.startsWith("```html")) {
767
+ cleanedHtml = cleanedHtml.substring(7);
768
+ }
769
+ if (cleanedHtml.endsWith("```")) {
770
+ cleanedHtml = cleanedHtml.substring(0, cleanedHtml.length - 3);
771
+ }
772
+ cleanedHtml = cleanedHtml.trim();
773
+ }
774
+
775
+ const html = cleanedHtml;
776
+
777
+ if(html && /<[a-z]/i.test(html)){
778
+ loadHTMLWithScripts(html);
779
+ scanPlaceholders();
780
+ sync();
781
+ pushState();
782
+ }
783
+
784
+ // Clear the input so the same file can be selected again
785
+ input.value = '';
786
+ };
787
+ reader.readAsText(file);
788
+ }
789
+
790
+ let colorChangeTimeout;
791
+ function setDocumentTextColor(color) {
792
+ const doc = frame.contentDocument;
793
+ const body = doc.body;
794
+
795
+ // Push state before first change only
796
+ if (!colorChangeTimeout) {
797
+ pushState();
798
+ }
799
+
800
+ body.style.color = color;
801
+ sync();
802
+
803
+ // Debounce pushState for rapid color changes
804
+ clearTimeout(colorChangeTimeout);
805
+ colorChangeTimeout = setTimeout(() => {
806
+ pushState();
807
+ colorChangeTimeout = null;
808
+ }, 500);
809
+ }
810
+
811
+ function setElementBackgroundColor(color) {
812
+ const doc = frame.contentDocument;
813
+ const selection = doc.getSelection();
814
+
815
+ // Push state before first change only
816
+ if (!colorChangeTimeout) {
817
+ pushState();
818
+ }
819
+
820
+ if (selectedElement) {
821
+ // If an element is selected via element selector, apply to that
822
+ selectedElement.style.backgroundColor = color;
823
+ } else if (selection.rangeCount > 0) {
824
+ // If there's a text selection, apply to the parent element
825
+ const range = selection.getRangeAt(0);
826
+ const container = range.commonAncestorContainer;
827
+ const element = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
828
+
829
+ if (element && element !== doc.body && element !== doc.documentElement) {
830
+ element.style.backgroundColor = color;
831
+ }
832
+ } else {
833
+ // No selection, apply to body as fallback
834
+ doc.body.style.backgroundColor = color;
835
+ }
836
+
837
+ sync();
838
+
839
+ // Debounce pushState for rapid color changes
840
+ clearTimeout(colorChangeTimeout);
841
+ colorChangeTimeout = setTimeout(() => {
842
+ pushState();
843
+ colorChangeTimeout = null;
844
+ }, 500);
845
+ }
846
+ </script>
847
+ </body>
848
  </html>