lastStyleClose && lastStyleOpen !== -1;
const isJS = lastScriptOpen > lastScriptClose && lastScriptOpen !== -1;
const firstLineStart = val.lastIndexOf('\n', start - 1) + 1;
let lastLineEnd = val.indexOf('\n', end); if (lastLineEnd === -1) lastLineEnd = val.length;
const block = val.substring(firstLineStart, lastLineEnd);
const lines = block.split('\n');
let newBlock;
let cursorOffset = 0;
// --- 2. APPLY CORRECT COMMENT FORMAT ---
if (isJS) {
const allCommented = lines.every(line => line.trim().startsWith('//'));
if (allCommented) {
newBlock = lines.map(line => {
return line.replace(/^(\s*)\/\//, '$1');
}).join('\n');
cursorOffset = -2;
} else {
newBlock = lines.map(line => {
const ind = line.match(/^(\s*)/)[1];
return ind + '//' + line.substring(ind.length);
}).join('\n');
cursorOffset = 2;
}
} else if (isCSS) {
const allCommented = lines.every(line => line.trim().startsWith('/*') && line.trim().endsWith('*/'));
if (allCommented) {
newBlock = lines.map(line => line.replace(/^(\s*)\/\*\s/, '$1').replace(/\s\*\/(\s*)$/, '$1')).join('\n');
cursorOffset = -3;
} else {
newBlock = lines.map(line => {
const ind = line.match(/^(\s*)/)[1];
return ind + '/* ' + line.substring(ind.length) + ' */';
}).join('\n');
cursorOffset = 3;
}
} else {
const allCommented = lines.every(line => line.trim().startsWith(''));
if (allCommented) {
newBlock = lines.map(line => line.replace('', '')).join('\n');
cursorOffset = -4;
} else {
newBlock = lines.map(line => { const ind = line.match(/^(\s*)/)[1]; return ind + ''; }).join('\n');
cursorOffset = 4;
}
}
// UPDATE TEXT & CURSOR ---
codeEditor.setRangeText(newBlock, firstLineStart, lastLineEnd, 'end');
if (start === end) {
const newCursorPos = start + cursorOffset;
codeEditor.selectionStart = codeEditor.selectionEnd = newCursorPos;
}
pushHistory(); triggerUpdate(); return;
}
const start = codeEditor.selectionStart; const end = codeEditor.selectionEnd; const val = codeEditor.value;
if (e.key === 'Tab') {
e.preventDefault(); const spaces = ' ';
const firstLineStart = val.lastIndexOf('\n', start - 1) + 1; let lastLineEnd = val.indexOf('\n', end); if (lastLineEnd === -1) lastLineEnd = val.length;
const block = val.substring(firstLineStart, lastLineEnd); const lines = block.split('\n'); let newBlock;
if (e.shiftKey) newBlock = lines.map(line => line.replace(/^(\t| {1,4})/, '')).join('\n');
else { if (start === end) { codeEditor.setRangeText(spaces, start, end, 'end'); pushHistory(); triggerUpdate(); return; } newBlock = lines.map(line => spaces + line).join('\n'); }
codeEditor.setRangeText(newBlock, firstLineStart, lastLineEnd, 'start'); pushHistory(); triggerUpdate(); return;
}
if (e.key === 'Enter') {
e.preventDefault();
const lineStart = val.lastIndexOf('\n', start - 1) + 1;
const currentIndent = (val.substring(lineStart, start).match(/^\s*/) || [''])[0];
let insertText = '\n' + currentIndent;
const charBefore = val.substring(start - 1, start);
const charAfter = val.substring(end, end + 1);
// 1. Handle Curly Braces { | }
if (charBefore === '{' && charAfter === '}') {
insertText = '\n' + currentIndent + ' \n' + currentIndent;
codeEditor.setRangeText(insertText, start, end, 'end');
codeEditor.selectionStart = codeEditor.selectionEnd = start + currentIndent.length + 5;
}
// 2. Handle Tags | (New Logic)
else if (charBefore === '>' && charAfter === '<' && val.substring(start, start + 2) === '') {
insertText = '\n' + currentIndent + ' \n' + currentIndent;
codeEditor.setRangeText(insertText, start, end, 'end');
codeEditor.selectionStart = codeEditor.selectionEnd = start + currentIndent.length + 5;
}
// 3. Standard Enter
else {
if (charBefore === '{') insertText += ' ';
codeEditor.setRangeText(insertText, start, end, 'end');
}
pushHistory(); triggerUpdate(); return;
}
const pairs = { '{': '}', '[': ']', '(': ')', '"': '"', "'": "'", '`': '`' };
if (pairs[e.key]) {
if (start !== end) { e.preventDefault(); const wrapped = e.key + val.substring(start, end) + pairs[e.key]; codeEditor.setRangeText(wrapped, start, end, 'end'); codeEditor.selectionStart = codeEditor.selectionEnd = start + 1; }
else { const nextChar = val.substring(start, start + 1); if (nextChar === e.key) { e.preventDefault(); codeEditor.selectionStart = codeEditor.selectionEnd = start + 1; } else { e.preventDefault(); codeEditor.setRangeText(e.key + pairs[e.key], start, end, 'start'); codeEditor.selectionStart = codeEditor.selectionEnd = start + 1; } }
pushHistory(); triggerUpdate(); return;
}
if (e.key === '>') {
const textBefore = val.substring(0, start); const tagStart = textBefore.lastIndexOf('<');
if (tagStart !== -1 && tagStart > textBefore.lastIndexOf('>')) {
const tagContent = val.substring(tagStart + 1, start);
const tagMatch = tagContent.match(/^\/?([a-zA-Z][a-zA-Z0-9-]*)/);
if (tagMatch && !tagMatch[0].startsWith('/')) {
const tagName = tagMatch[1].toLowerCase();
if (!VOID_ELEMENTS.has(tagName)) { e.preventDefault(); codeEditor.setRangeText(`>${tagName}>`, start, end, 'end'); codeEditor.selectionStart = codeEditor.selectionEnd = start + 1; pushHistory(); triggerUpdate(); return; }
}
}
}
});
codeEditor.addEventListener('input', () => { pushHistory(); triggerUpdate(); });
codeEditor.addEventListener('scroll', syncScroll, { passive: true });
function triggerUpdate() { updateCharCount(); scheduleHighlight(); schedulePreview(); scheduleSave(); }
function scheduleHighlight() {
if (highlightRAF) cancelAnimationFrame(highlightRAF);
highlightRAF = requestAnimationFrame(() => { updateHighlight(); syncScroll(); highlightRAF = null; });
}
function schedulePreview() {
if (totalChars > 90000) {
if (liveMode) {
liveMode = false;
btnLive.classList.remove('active');
const lastStatus = statusText.textContent;
statusText.textContent = 'Large file: Live Mode disabled';
setTimeout(() => statusText.textContent = lastStatus, 5000);
}
clearTimeout(previewDebounceTimer); return;
}
let delay = 0; if (totalChars > 60000) delay = 200; else if (totalChars > 30000) delay = 50;
clearTimeout(previewDebounceTimer);
if (liveMode) { if (delay === 0) updatePreview(); else previewDebounceTimer = setTimeout(updatePreview, delay); }
}
function scheduleSave() { clearTimeout(saveTimer); saveTimer = setTimeout(() => { localStorage.setItem('editor_code', codeEditor.value); }, 500); }
// --- App Logic ---
const fallbackHTML =
`
Fallback HTML
Fallback HTML
Drag the center bar to resize.
`;
// If not running on a PHP server, replace this section with the fallback HTML.
const defaultHTML = "\n\n\n\n\n\n \n \n \n Window Title<\/title>\n