(() => {
  let editorTree = null;
  let editorOpenFile = '';
  let editorLoadedText = '';
  const editorCollapsedTree = new Set();
  let editorBooted = false;

  let editorCollapsedMode = false;
  let editorOutlineMode = 'section';
  const EDITOR_OUTLINE_MODES = ['section', 'standard', 'indent'];
  const EDITOR_OUTLINE_LABELS = { section: 'Section Titles', standard: 'Standard', indent: 'Indentation' };
  let editorCollapsedBlocks = [];
  let editorCollapsedSelection = null;
  let editorCollapsedAnchor = null;
  let editorExpandedCollapsedBlock = null;
  let editorLastRootRequest = '';
  let editorContextFilePath = '';
  let editorContextFolderPath = '';

  function byId(id) {
    return document.getElementById(id);
  }

  let editorRuntimeRootLabel = 'workspace';

  function getEditorRootRequest() {
    return '';
  }

  function getEditorRootLabel() {
    return editorRuntimeRootLabel || 'workspace';
  }

  function setEditorStatus(text) {
    const el = byId('editor-file-status');
    if (el) el.textContent = text;
  }

  function currentEditorText() {
    return byId('editor-textarea')?.value || '';
  }

  function editorIsDirty() {
    return !!editorOpenFile && currentEditorText() !== editorLoadedText;
  }

  function escapeCodeHtml(text) {
    return String(text ?? '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');
  }

  const EDITOR_BLOCKED_EXTENSIONS = new Set([
    '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.svg',
    '.mp4', '.mov', '.avi', '.mkv', '.webm', '.mp3', '.wav', '.ogg',
    '.zip', '.rar', '.7z', '.tar', '.gz', '.exe', '.dll', '.bin', '.dat',
    '.pdf', '.woff', '.woff2', '.ttf', '.otf'
  ]);

  function editorFileExtension(filePath) {
    const clean = String(filePath || '').split('?')[0].split('#')[0].toLowerCase();
    const name = clean.split('/').pop() || '';
    const dot = name.lastIndexOf('.');
    return dot >= 0 ? name.slice(dot) : '';
  }

  function isEditorEditableFile(filePath) {
    const ext = editorFileExtension(filePath);
    if (!ext) return true;
    return !EDITOR_BLOCKED_EXTENSIONS.has(ext);
  }

  function showEditorNonEditableFile(filePath) {
    editorOpenFile = '';
    editorLoadedText = '';
    editorCollapsedMode = false;
    editorCollapsedSelection = null;
    editorCollapsedAnchor = null;
    editorExpandedCollapsedBlock = null;
    const textarea = byId('editor-textarea');
    if (textarea) textarea.value = '';
    const code = byId('editor-highlight-code');
    if (code) code.textContent = `Not editable in Verbatim editor:\n${filePath}\n\nBinary and media files are blocked to prevent freezing.`;
    setEditorStatus('Not editable · binary/media file blocked');
    toast('Binary/media file blocked from editor');
    renderEditorTree();
    refreshEditorHeader();
  }

  function detectEditorLanguage(filePath) {
    const p = String(filePath || '').toLowerCase();
    if (p.endsWith('.ts') || p.endsWith('.tsx') || p.endsWith('.js') || p.endsWith('.jsx')) return 'script';
    if (p.endsWith('.json')) return 'json';
    if (p.endsWith('.html') || p.endsWith('.htm')) return 'html';
    if (p.endsWith('.css')) return 'css';
    return 'plain';
  }

  function highlightEditorCode(text, language) {
    let html = escapeCodeHtml(text);

    if (language === 'script') {
      html = html
        .replace(/(\/\/.*?$)/gm, '<span class="tok-comment">$1</span>')
        .replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="tok-comment">$1</span>')
        .replace(/(["'`])(?:\\.|(?!\1)[\s\S])*\1/g, '<span class="tok-string">$&</span>')
        .replace(/\b(const|let|var|function|return|if|else|for|while|switch|case|break|continue|try|catch|finally|throw|new|await|async|import|from|export|default|class|extends|implements|type|interface)\b/g, '<span class="tok-keyword">$1</span>')
        .replace(/\b(string|number|boolean|void|any|unknown|never|object|Promise|Record|Array)\b/g, '<span class="tok-type">$1</span>')
        .replace(/\b(\d+(?:\.\d+)?)\b/g, '<span class="tok-number">$1</span>')
        .replace(/([{}()[\];,.])/g, '<span class="tok-punct">$1</span>');
    } else if (language === 'json') {
      html = html
        .replace(/("(?:\\.|[^"\\])*?")\s*:/g, '<span class="tok-prop">$1</span><span class="tok-punct">:</span>')
        .replace(/:\s*("(?:\\.|[^"\\])*?")/g, ': <span class="tok-string">$1</span>')
        .replace(/\b(true|false|null)\b/g, '<span class="tok-keyword">$1</span>')
        .replace(/\b(\d+(?:\.\d+)?)\b/g, '<span class="tok-number">$1</span>')
        .replace(/([{}\[\],])/g, '<span class="tok-punct">$1</span>');
    } else if (language === 'html') {
      html = html
        .replace(/(&lt;\/?)([a-zA-Z0-9:-]+)/g, '$1<span class="tok-tag">$2</span>')
        .replace(/\s([a-zA-Z-:]+)(=)/g, ' <span class="tok-attr">$1</span><span class="tok-punct">$2</span>')
        .replace(/("(?:\\.|[^"\\])*?")/g, '<span class="tok-string">$1</span>')
        .replace(/(&lt;|&gt;|\/&gt;)/g, '<span class="tok-punct">$1</span>');
    } else if (language === 'css') {
      html = html
        .replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="tok-comment">$1</span>')
        .replace(/([.#]?[a-zA-Z0-9_-]+)(\s*\{)/g, '<span class="tok-tag">$1</span>$2')
        .replace(/([a-zA-Z-]+)(\s*:)/g, '<span class="tok-prop">$1</span>$2')
        .replace(/(:\s*)([^;{}]+)(;?)/g, '$1<span class="tok-string">$2</span>$3')
        .replace(/([{}:;,.])/g, '<span class="tok-punct">$1</span>');
    }

    return html || ' ';
  }

  function syncEditorHighlight() {
    const textarea = byId('editor-textarea');
    const highlight = byId('editor-highlight');
    const code = byId('editor-highlight-code');
    if (!textarea || !highlight || !code || editorCollapsedMode) return;
    code.innerHTML = highlightEditorCode(textarea.value, detectEditorLanguage(editorOpenFile));
    highlight.scrollTop = textarea.scrollTop;
    highlight.scrollLeft = textarea.scrollLeft;
  }

  function setCollapsedMode(next) {
    editorCollapsedMode = Boolean(next && editorCollapsedBlocks.length);
    const stage = byId('editor-stage');
    if (stage) stage.classList.toggle('collapsed-mode', editorCollapsedMode);
    const collapsed = byId('editor-collapsed-view');
    if (collapsed) collapsed.hidden = !editorCollapsedMode;
    if (!editorCollapsedMode) syncEditorHighlight();
    refreshEditorHeader();
  }

  function refreshEditorHeader() {
    const rootEl = byId('editor-root-label');
    const pathEl = byId('editor-file-path');
    if (rootEl) rootEl.textContent = getEditorRootLabel();
    if (pathEl) pathEl.textContent = editorOpenFile || 'No file selected';

    if (!editorOpenFile) setEditorStatus('No file selected');
    else if (editorCollapsedMode) setEditorStatus(`Collapsed outline · ${editorCollapsedBlocks.length} blocks`);
    else if (editorIsDirty()) setEditorStatus('Unsaved changes');
    else setEditorStatus('Saved');

    syncEditorHighlight();
  }

  function normalizeEditorPath(value) {
    const raw = String(value || '').trim().replace(/\\/g, '/');
    if (!raw) return '';
    if (raw.startsWith('./') || raw.startsWith('/') || /^[A-Za-z]:\//.test(raw)) return raw;
    if (raw.startsWith('projects/')) return `./${raw}`;
    return raw;
  }

  function canonicalEditorFileSegment(value) {
    return String(value || '')
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9._-]+/g, '_')
      .replace(/^_+|_+$/g, '');
  }

  function buildCanonicalEditorNewFilePath(rawPath) {
    const input = String(rawPath || '').trim().replace(/\\/g, '/');
    if (!input || input.startsWith('/') || /^[A-Za-z]:\//.test(input) || input.split('/').includes('..')) return '';

    const relative = input
      .replace(/^\.\//, '')
      .split('/')
      .map(canonicalEditorFileSegment)
      .filter(Boolean)
      .join('/');

    if (!relative) return '';

    const root = getEditorRootRequest().replace(/[\\/]+$/, '');
    return normalizeEditorPath(`${root}/${relative}`);
  }

  function normalizeEditorChildNode(parentPath, child) {
    const parent = normalizeEditorPath(parentPath).replace(/\/$/, '');
    const childPath = normalizeEditorPath(child?.path || '');
    const childName = String(child?.name || '').trim();
    let nextPath = childPath;

    if (!nextPath && childName) nextPath = `${parent}/${childName}`;
    else if (childName && nextPath === childName) nextPath = `${parent}/${childName}`;
    else if (nextPath && !nextPath.startsWith('./') && !nextPath.startsWith('/') && !/^[A-Za-z]:\//.test(nextPath)) nextPath = normalizeEditorPath(nextPath);

    return { ...child, path: nextPath };
  }

  function renderEditorTreeNode(node) {
    const wrap = document.createElement('div');
    wrap.className = 'editor-tree-node' + (editorCollapsedTree.has(node.path) ? ' collapsed' : '');

    const row = document.createElement('div');
    row.className = 'editor-tree-row' + (node.path === editorOpenFile ? ' active' : '');

    const caret = document.createElement('span');
    caret.className = 'editor-tree-caret';
    caret.textContent = node.type === 'dir' ? (editorCollapsedTree.has(node.path) ? '+' : '-') : '';

    const type = document.createElement('span');
    type.className = 'editor-tree-type';
    type.textContent = node.type === 'dir' ? '□' : '·';

    const name = document.createElement('span');
    name.className = 'editor-tree-name';
    name.textContent = node.name;

    row.appendChild(caret);
    row.appendChild(type);
    row.appendChild(name);
    wrap.appendChild(row);

    if (node.type === 'dir') {
      row.addEventListener('contextmenu', event => openEditorContextMenu(event, '', node.path));

      row.addEventListener('click', async () => {
        const isCollapsed = editorCollapsedTree.has(node.path);

        if (node.lazy && isCollapsed) {
          const data = await api('/read/tree', 'POST', { root: node.path });
          if (!data?.ok || !data.root) {
            console.warn('[editor-tree] lazy folder failed', node.path, data);
            toast(`Unable to load folder: ${node.name || node.path}`);
            return;
          }
          node.children = Array.isArray(data.root.children)
            ? data.root.children.map(child => {
                const normalized = normalizeEditorChildNode(node.path, child);
                return {
                  ...normalized,
                  lazy: normalized?.type === 'dir' && (!Array.isArray(normalized.children) || !normalized.children.length)
                };
              })
            : [];

          node.lazy = false;
        }

        if (isCollapsed) editorCollapsedTree.delete(node.path);
        else editorCollapsedTree.add(node.path);
        renderEditorTree();
      });

      const children = document.createElement('div');
      children.className = 'editor-tree-children';
      for (const child of node.children || []) children.appendChild(renderEditorTreeNode(child));
      wrap.appendChild(children);
    } else {
      row.addEventListener('click', () => openEditorFile(node.path));
      row.addEventListener('contextmenu', event => openEditorContextMenu(event, node.path));
    }

    return wrap;
  }

  function renderEditorTree() {
    const tree = byId('editor-tree');
    if (!tree) return;
    tree.innerHTML = '';

    if (!editorTree) {
      tree.innerHTML = '<div style="color:var(--text3);padding:6px">Load the project tree to begin.</div>';
      return;
    }

    if (editorTree.path === '__editor_projects__') {
      const header = document.createElement('div');
      header.className = 'editor-tree-root-heading';
      header.textContent = 'Projects';
      tree.appendChild(header);
      for (const child of editorTree.children || []) tree.appendChild(renderEditorTreeNode(child));
      return;
    }

    tree.appendChild(renderEditorTreeNode(editorTree));
  }

  function getLineOffsets(text) {
    const offsets = [0];
    for (let i = 0; i < text.length; i++) if (text[i] === '\n') offsets.push(i + 1);
    return offsets;
  }

  function indexToLineNumber(offsets, index) {
    let low = 0;
    let high = offsets.length - 1;
    while (low <= high) {
      const mid = Math.floor((low + high) / 2);
      if (offsets[mid] <= index) low = mid + 1;
      else high = mid - 1;
    }
    return Math.max(1, high + 1);
  }

  function scanBlockEnd(text, openIndex) {
    let depth = 0;
    let quote = '';
    let inLineComment = false;
    let inBlockComment = false;
    let escaped = false;

    for (let i = openIndex; i < text.length; i++) {
      const ch = text[i];
      const next = text[i + 1];

      if (inLineComment) {
        if (ch === '\n') inLineComment = false;
        continue;
      }
      if (inBlockComment) {
        if (ch === '*' && next === '/') {
          inBlockComment = false;
          i++;
        }
        continue;
      }
      if (quote) {
        if (escaped) {
          escaped = false;
          continue;
        }
        if (ch === '\\') {
          escaped = true;
          continue;
        }
        if (ch === quote) quote = '';
        continue;
      }
      if (ch === '/' && next === '/') {
        inLineComment = true;
        i++;
        continue;
      }
      if (ch === '/' && next === '*') {
        inBlockComment = true;
        i++;
        continue;
      }
      if (ch === '"' || ch === '\'' || ch === '`') {
        quote = ch;
        continue;
      }
      if (ch === '{') depth++;
      else if (ch === '}') {
        depth--;
        if (depth === 0) {
          let end = i + 1;
          while (end < text.length && /[;\s]/.test(text[end])) end++;
          return end;
        }
      }
    }
    return -1;
  }

  function makeCollapsedBlock(signature, startIndex, endIndex, offsets, extra = {}) {
    return {
      index: 0,
      startIndex,
      endIndex,
      startLine: indexToLineNumber(offsets, startIndex),
      signature,
      ...extra
    };
  }

  function finalizeCollapsedBlocks(blocks) {
    return blocks
      .filter(block => block && Number.isFinite(block.startIndex) && Number.isFinite(block.endIndex) && block.endIndex > block.startIndex)
      .sort((a, b) => a.startIndex - b.startIndex)
      .map((block, index) => ({ ...block, index }));
  }

  function collapsedLineHasContent(text, startIndex, endIndex) {
    return String(text || '').slice(startIndex, endIndex).trim().length > 0;
  }

  function nameScriptBlock(line) {
    const clean = String(line || '').trim();
    const functionName = clean.match(/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/)?.[1];
    if (functionName) return `function/${functionName}`;
    const className = clean.match(/(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/)?.[1];
    if (className) return `class/${className}`;
    const arrowName = clean.match(/(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/)?.[1];
    if (arrowName) return `function/${arrowName}`;
    return '(script)';
  }

  function analyzeScriptBlocks(text, filePath) {
    const source = String(text || '');
    const lines = source.split('\n');
    const offsets = getLineOffsets(source);
    const named = [];
    const patterns = [
      /^\s*export\s+async\s+function\s+[A-Za-z_$][\w$]*/,
      /^\s*export\s+function\s+[A-Za-z_$][\w$]*/,
      /^\s*async\s+function\s+[A-Za-z_$][\w$]*/,
      /^\s*function\s+[A-Za-z_$][\w$]*/,
      /^\s*export\s+class\s+[A-Za-z_$][\w$]*/,
      /^\s*class\s+[A-Za-z_$][\w$]*/,
      /^\s*export\s+(const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/,
      /^\s*(const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/
    ];

    let nextAllowedLine = 0;
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      if (lineIndex < nextAllowedLine) continue;
      const line = lines[lineIndex];
      if (/^\s/.test(line)) continue;
      if (!patterns.some(pattern => pattern.test(line))) continue;

      const startIndex = offsets[lineIndex];
      const braceIndex = source.indexOf('{', startIndex);
      if (braceIndex < 0) continue;
      const endIndex = scanBlockEnd(source, braceIndex);
      if (endIndex < 0) continue;

      nextAllowedLine = indexToLineNumber(offsets, endIndex) - 1;
      named.push(makeCollapsedBlock(nameScriptBlock(line), startIndex, endIndex, offsets));
    }

    if (!named.length) {
      return source.trim() ? finalizeCollapsedBlocks([makeCollapsedBlock('section/script', 0, source.length, offsets)]) : [];
    }

    const blocks = [];
    const first = named[0];
    const last = named[named.length - 1];

    if (collapsedLineHasContent(source, 0, first.startIndex)) {
      blocks.push(makeCollapsedBlock('section/imports', 0, first.startIndex, offsets));
    }

    const runtimeSegments = [];
    named.forEach((block, i) => {
      blocks.push(block);
      const next = named[i + 1];
      const gapStart = block.endIndex;
      const gapEnd = next ? next.startIndex : last.endIndex;
      if (next && collapsedLineHasContent(source, gapStart, gapEnd)) runtimeSegments.push({ startIndex: gapStart, endIndex: gapEnd });
    });

    if (runtimeSegments.length) {
      blocks.push(makeCollapsedBlock('section/script', runtimeSegments[0].startIndex, runtimeSegments[runtimeSegments.length - 1].endIndex, offsets, { segments: runtimeSegments }));
    }

    if (collapsedLineHasContent(source, last.endIndex, source.length)) {
      blocks.push(makeCollapsedBlock('section/trailing', last.endIndex, source.length, offsets));
    }

    return finalizeCollapsedBlocks(blocks);
  }

  function compactSelectorLabel(selector) {
    return String(selector || '')
      .replace(/\/\*[\s\S]*?\*\//g, '')
      .replace(/\s+/g, ' ')
      .trim()
      .slice(0, 120) || 'CSS Block';
  }

  function analyzeCssBlocks(text) {
    const source = String(text || '');
    const offsets = getLineOffsets(source);
    const blocks = [];
    const named = [];
    let searchIndex = 0;

    while (searchIndex < source.length) {
      const openIndex = source.indexOf('{', searchIndex);
      if (openIndex < 0) break;
      const lineStart = source.lastIndexOf('\n', openIndex) + 1;
      const selectorStart = Math.max(source.lastIndexOf('}', openIndex) + 1, lineStart);
      const selector = source.slice(selectorStart, openIndex).trim();
      const endIndex = scanBlockEnd(source, openIndex);
      if (!selector || endIndex < 0) { searchIndex = openIndex + 1; continue; }
      named.push(makeCollapsedBlock(compactSelectorLabel(selector), selectorStart, endIndex, offsets));
      searchIndex = endIndex;
    }

    if (!named.length) {
      return source.trim() ? finalizeCollapsedBlocks([makeCollapsedBlock('section/script', 0, source.length, offsets)]) : [];
    }

    const first = named[0];
    const last = named[named.length - 1];
    if (collapsedLineHasContent(source, 0, first.startIndex)) blocks.push(makeCollapsedBlock('section/imports', 0, first.startIndex, offsets));
    blocks.push(...named);
    if (collapsedLineHasContent(source, last.endIndex, source.length)) blocks.push(makeCollapsedBlock('section/trailing', last.endIndex, source.length, offsets));
    return finalizeCollapsedBlocks(blocks);
  }

  function htmlElementLabel(rawTag) {
    const tagText = String(rawTag || '');
    const tag = tagText.match(/^<\s*([a-zA-Z0-9:-]+)/)?.[1] || 'element';
    const id = tagText.match(/\sid\s*=\s*["']([^"']+)["']/i)?.[1];
    const className = tagText.match(/\sclass\s*=\s*["']([^"']+)["']/i)?.[1];
    if (id) return `section/${id}`;
    if (className) return `section/${String(className).trim().split(/\s+/)[0]}`;
    return `section/${tag}`;
  }

  function analyzeHtmlBlocks(text) {
    const source = String(text || '');
    const offsets = getLineOffsets(source);
    const tagPattern = /<\/?[a-zA-Z][^>]*?>/g;
    const stack = [];
    const blocks = [];
    const voidTags = new Set(['area','base','br','col','embed','hr','img','input','link','meta','param','source','track','wbr']);
    let match;

    while ((match = tagPattern.exec(source))) {
      const raw = match[0];
      if (/^<\//.test(raw)) {
        const closing = raw.match(/^<\/\s*([a-zA-Z0-9:-]+)/)?.[1]?.toLowerCase();
        for (let i = stack.length - 1; i >= 0; i--) {
          if (stack[i].tag === closing) {
            const item = stack.splice(i, 1)[0];
            const endIndex = tagPattern.lastIndex;
            if (item.depth <= 3) blocks.push(makeCollapsedBlock(item.label, item.startIndex, endIndex, offsets));
            break;
          }
        }
        continue;
      }

      const tag = raw.match(/^<\s*([a-zA-Z0-9:-]+)/)?.[1]?.toLowerCase();
      if (!tag || raw.endsWith('/>') || voidTags.has(tag)) continue;
      stack.push({ tag, startIndex: match.index, depth: stack.length + 1, label: htmlElementLabel(raw) });
    }

    for (const item of stack) {
      if (item.depth <= 3) blocks.push(makeCollapsedBlock(item.label, item.startIndex, source.length, offsets));
    }

    return finalizeCollapsedBlocks(blocks.length ? blocks : (source.trim() ? [makeCollapsedBlock('section/script', 0, source.length, offsets)] : []));
  }

  function cleanEditorSectionTitle(line) {
    const raw = String(line || '').trim();
    if (!raw) return '';
    const cleaned = raw
      .replace(/^\/\/\s?/, '')
      .replace(/^#\s?/, '')
      .replace(/^\/\*+\s?/, '')
      .replace(/^\*\s?/, '')
      .replace(/\s?\*+\/$/, '')
      .replace(/^<!--\s?/, '')
      .replace(/\s?-->$/, '')
      .replace(/^[-=]{3,}\s?/, '')
      .replace(/\s?[-=]{3,}$/, '')
      .trim();
    return cleaned.slice(0, 96);
  }

  function isEditorSectionTitleLine(line) {
    const raw = String(line || '');
    const trimmed = raw.trim();
    if (!trimmed) return false;
    if (/^(\/\/|#|\/\*+|\*|<!--)\s*\S/.test(trimmed)) return !!cleanEditorSectionTitle(trimmed);
    if (/^[-=]{3,}\s*\S/.test(trimmed)) return true;
    return false;
  }

  function analyzeSectionTitleBlocks(text) {
    const source = String(text || '');
    const lines = source.split('\n');
    const offsets = getLineOffsets(source);
    const anchors = [];

    lines.forEach((line, lineIndex) => {
      if (!isEditorSectionTitleLine(line)) return;
      const label = cleanEditorSectionTitle(line) || `line ${lineIndex + 1}`;
      anchors.push({ lineIndex, startIndex: offsets[lineIndex], label });
    });

    if (anchors.length < 2) return [];

    const blocks = anchors.map((anchor, i) => {
      const next = anchors[i + 1];
      return makeCollapsedBlock(`section/${anchor.label}`, anchor.startIndex, next ? next.startIndex : source.length, offsets);
    });

    return finalizeCollapsedBlocks(blocks);
  }

  function analyzeIndentBlocks(text) {
    const source = String(text || '');
    const lines = source.split('\n');
    const offsets = getLineOffsets(source);
    const anchors = [];

    lines.forEach((line, lineIndex) => {
      if (!line.trim()) return;
      if (/^\s/.test(line)) return;
      anchors.push({ lineIndex, startIndex: offsets[lineIndex], label: line.trim().slice(0, 96) });
    });

    if (anchors.length < 2) return [];

    const blocks = anchors.map((anchor, i) => {
      const next = anchors[i + 1];
      return makeCollapsedBlock(`indent/${anchor.label}`, anchor.startIndex, next ? next.startIndex : source.length, offsets);
    });

    return finalizeCollapsedBlocks(blocks);
  }

  function analyzeTopLevelBlocks(text, filePath) {
    const language = detectEditorLanguage(filePath);
    const standardBlocks = () => {
      if (language === 'script') return analyzeScriptBlocks(text, filePath);
      if (language === 'css') return analyzeCssBlocks(text);
      if (language === 'html') return analyzeHtmlBlocks(text);
      const source = String(text || '');
      const offsets = getLineOffsets(source);
      return source.trim() ? finalizeCollapsedBlocks([makeCollapsedBlock('section/script', 0, source.length, offsets)]) : [];
    };

    if (editorOutlineMode === 'section') {
      const sectionBlocks = analyzeSectionTitleBlocks(text);
      return sectionBlocks.length ? sectionBlocks : standardBlocks();
    }

    if (editorOutlineMode === 'indent') {
      const indentBlocks = analyzeIndentBlocks(text);
      return indentBlocks.length ? indentBlocks : standardBlocks();
    }

    return standardBlocks();
  }

  function updateCollapsedBlocks() {
    editorCollapsedBlocks = analyzeTopLevelBlocks(currentEditorText(), editorOpenFile);
    if (!editorCollapsedBlocks.length) {
      editorExpandedCollapsedBlock = null;
      editorCollapsedSelection = null;
      editorCollapsedAnchor = null;
      setCollapsedMode(false);
    } else if (editorCollapsedSelection?.start >= editorCollapsedBlocks.length) {
      editorCollapsedSelection = { start: editorCollapsedBlocks.length - 1, end: editorCollapsedBlocks.length - 1 };
      editorCollapsedAnchor = editorCollapsedBlocks.length - 1;
    }
    refreshSectionMenu();
  }

  function syncEditorSectionControl() {
    const select = byId('editor-section-select');
    if (!select) return;
    const selected = editorCollapsedSelection?.start;
    select.value = selected == null ? '' : String(selected);
  }

  function setEditorBlockSelection(index, opts = {}) {
    if (!editorCollapsedBlocks.length) return;
    const next = Math.max(0, Math.min(editorCollapsedBlocks.length - 1, Number(index) || 0));

    if (opts.extend && editorCollapsedAnchor != null) {
      editorCollapsedSelection = { start: editorCollapsedAnchor, end: next };
    } else {
      editorCollapsedAnchor = next;
      editorCollapsedSelection = { start: next, end: next };
    }

    syncEditorSectionControl();
    if (editorCollapsedMode) renderCollapsedView();
    if (opts.jump) jumpToEditorBlock(next);
  }

  function renderCollapsedView() {
    const host = byId('editor-collapsed-view');
    if (!host) return;
    host.innerHTML = '';
    if (!editorCollapsedBlocks.length) {
      host.innerHTML = '<div class="editor-collapsed-empty">No top-level function, class, or export blocks found in this file.</div>';
      return;
    }

    const start = editorCollapsedSelection ? Math.min(editorCollapsedSelection.start, editorCollapsedSelection.end) : -1;
    const end = editorCollapsedSelection ? Math.max(editorCollapsedSelection.start, editorCollapsedSelection.end) : -1;

    editorCollapsedBlocks.forEach(block => {
      const row = document.createElement('div');
      row.className = 'editor-collapsed-item';
      if (block.index >= start && block.index <= end) row.classList.add('selected');
      row.dataset.index = String(block.index);

      const line = document.createElement('span');
      line.className = 'editor-collapsed-line';
      line.textContent = String(block.startLine);

      const caret = document.createElement('span');
      caret.className = 'editor-collapsed-caret';
      caret.textContent = '▸';

      const signature = document.createElement('span');
      signature.className = 'editor-collapsed-signature';
      signature.textContent = block.signature;

      row.appendChild(line);
      row.appendChild(caret);
      row.appendChild(signature);

      if (block.index === editorExpandedCollapsedBlock) {
        const source = document.createElement('pre');
        source.className = 'editor-collapsed-source';
        const text = currentEditorText();
        source.textContent = Array.isArray(block.segments) && block.segments.length
          ? block.segments.map(segment => text.slice(segment.startIndex, segment.endIndex)).join('\n')
          : text.slice(block.startIndex, block.endIndex);
        row.appendChild(source);
      }

      row.addEventListener('click', () => {
        const index = Number(row.dataset.index);
        editorExpandedCollapsedBlock = editorExpandedCollapsedBlock === index ? null : index;
        setEditorBlockSelection(index);
      });

      row.addEventListener('dblclick', () => {
        setEditorBlockSelection(Number(row.dataset.index), { jump: true });
      });

      row.addEventListener('contextmenu', event => openEditorContextMenu(event));
      host.appendChild(row);
    });
  }

  function getCollapsedSourceSelection() {
    if (!editorCollapsedSelection || !editorCollapsedBlocks.length) return '';
    const start = Math.min(editorCollapsedSelection.start, editorCollapsedSelection.end);
    const end = Math.max(editorCollapsedSelection.start, editorCollapsedSelection.end);
    const first = editorCollapsedBlocks[start];
    const last = editorCollapsedBlocks[end];
    if (!first || !last) return '';
    const text = currentEditorText();
    const selected = editorCollapsedBlocks.slice(start, end + 1);
    return selected.map(block => {
      if (Array.isArray(block.segments) && block.segments.length) {
        return block.segments.map(segment => text.slice(segment.startIndex, segment.endIndex)).join('\n');
      }
      return text.slice(block.startIndex, block.endIndex);
    }).join('\n');
  }

  function getCurrentEditorSelectionText() {
    const textarea = byId('editor-textarea');
    if (!textarea) return '';
    if (editorCollapsedMode) return getCollapsedSourceSelection();
    return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
  }

  async function copyEditorSelection(useFullFile = false) {
    const text = useFullFile ? currentEditorText() : (getCurrentEditorSelectionText() || currentEditorText());
    if (!text) {
      toast('Nothing to copy');
      return;
    }
    await navigator.clipboard.writeText(text);
    toast(useFullFile ? 'Full file copied' : 'Copied');
  }

  function insertIntoTextarea(text) {
    const textarea = byId('editor-textarea');
    if (!textarea) return;
    const start = textarea.selectionStart;
    const before = textarea.value.slice(0, start);
    const after = textarea.value.slice(textarea.selectionEnd);
    textarea.value = before + text + after;
    const next = start + text.length;
    textarea.setSelectionRange(next, next);
    textarea.focus();
    refreshEditorHeader();
    updateCollapsedBlocks();
    if (editorCollapsedMode) renderCollapsedView();
  }

  async function cutEditorSelection() {
    if (editorCollapsedMode) {
      toast('Cut is unavailable in collapsed view');
      return;
    }
    const textarea = byId('editor-textarea');
    if (!textarea) return;
    const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
    if (!selected) {
      toast('Select text first');
      return;
    }
    await navigator.clipboard.writeText(selected);
    textarea.setRangeText('', textarea.selectionStart, textarea.selectionEnd, 'start');
    refreshEditorHeader();
    updateCollapsedBlocks();
    toast('Cut');
  }

  async function pasteIntoEditor() {
    if (editorCollapsedMode) {
      toast('Expand the editor to paste');
      return;
    }
    const text = await navigator.clipboard.readText();
    if (!text) return;
    insertIntoTextarea(text);
    toast('Pasted');
  }

  async function runEditorSystemFileAction(action, filePath, payload) {
    const data = await api('/editor/system-action', 'POST', { action, filePath, payload });
    if (!data?.ok) throw new Error(data?.message || data?.error || `${action} failed`);
    return data;
  }

  function getEditorFileName(filePath) {
    return String(filePath || '').replace(/\\/g, '/').split('/').pop() || '';
  }

  function joinEditorPathParts(folderPath, fileName) {
    const folder = normalizeEditorPath(folderPath).replace(/[\\/]+$/, '');
    const name = canonicalEditorFileSegment(fileName);
    return normalizeEditorPath(`${folder}/${name}`);
  }

  function collectEditorFolderOptions(node, out = []) {
    if (!node) return out;
    if (node.type === 'dir' && node.path !== '__editor_projects__') out.push({ name: node.name, path: node.path });
    for (const child of node.children || []) collectEditorFolderOptions(child, out);
    return out;
  }

  function ensureEditorDestinationPicker() {
    let modal = byId('editor-destination-picker');
    if (modal) return modal;

    modal = document.createElement('div');
    modal.id = 'editor-destination-picker';
    modal.hidden = true;
    modal.style.position = 'fixed';
    modal.style.inset = '0';
    modal.style.zIndex = '9999';
    modal.style.background = 'rgba(0,0,0,.45)';
    modal.innerHTML = [
      '<div class="editor-destination-card" style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:min(560px,92vw);max-height:82vh;overflow:auto;background:var(--panel,#111);color:var(--text,#eee);border:1px solid var(--line,#444);border-radius:10px;padding:14px;box-shadow:0 18px 50px rgba(0,0,0,.45)">',
      '<div style="font-weight:700;margin-bottom:8px" id="editor-destination-title">Choose destination</div>',
      '<div style="font-size:12px;color:var(--text3,#aaa);margin-bottom:8px" id="editor-destination-source"></div>',
      '<label style="display:block;font-size:12px;margin:8px 0 4px">File name</label>',
      '<input id="editor-destination-name" style="width:100%;box-sizing:border-box;margin-bottom:10px" />',
      '<div style="font-size:12px;color:var(--text3,#aaa);margin-bottom:4px">Click a destination folder</div>',
      '<div id="editor-destination-folders" style="max-height:360px;overflow:auto;border:1px solid var(--line,#444);border-radius:8px;padding:6px"></div>',
      '<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:12px">',
      '<button type="button" id="editor-destination-cancel">Cancel</button>',
      '<button type="button" id="editor-destination-apply">Apply</button>',
      '</div>',
      '</div>'
    ].join('');
    document.body.appendChild(modal);
    return modal;
  }

  async function chooseEditorDestination(action, sourcePath) {
    const modal = ensureEditorDestinationPicker();
    const title = byId('editor-destination-title');
    const source = byId('editor-destination-source');
    const nameInput = byId('editor-destination-name');
    const folders = byId('editor-destination-folders');
    const cancel = byId('editor-destination-cancel');
    const apply = byId('editor-destination-apply');
    let selectedFolder = '';

    if (title) title.textContent = action === 'copy' ? 'Copy file to folder' : 'Move file to folder';
    if (source) source.textContent = sourcePath;
    if (nameInput) nameInput.value = getEditorFileName(sourcePath);
    if (folders) folders.innerHTML = '';

    if (!editorTree) await loadEditorTree();
    const options = collectEditorFolderOptions(editorTree);
    options.forEach(option => {
      const row = document.createElement('button');
      row.type = 'button';
      row.textContent = option.path;
      row.style.display = 'block';
      row.style.width = '100%';
      row.style.textAlign = 'left';
      row.style.margin = '2px 0';
      row.dataset.path = option.path;
      row.addEventListener('click', () => {
        selectedFolder = option.path;
        folders.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
        row.classList.add('active');
      });
      folders.appendChild(row);
    });

    modal.hidden = false;

    return await new Promise(resolve => {
      const done = value => {
        modal.hidden = true;
        cancel.onclick = null;
        apply.onclick = null;
        resolve(value);
      };
      cancel.onclick = () => done(null);
      apply.onclick = () => {
        const fileName = String(nameInput?.value || '').trim();
        if (!selectedFolder) { toast('Choose a destination folder'); return; }
        if (!fileName || fileName.includes('/') || fileName.includes('\\')) { toast('Enter only a file name'); return; }
        done(joinEditorPathParts(selectedFolder, fileName));
      };
    });
  }

  async function renameEditorTreeFile(filePath) {
    const currentName = String(filePath || '').split('/').pop() || '';
    const nextName = prompt('Rename file to:', currentName);
    if (nextName == null) return;
    const clean = String(nextName || '').trim();
    if (!clean || clean.includes('/') || clean.includes('\\')) { toast('Enter only a file name'); return; }
    const data = await runEditorSystemFileAction('rename', filePath, { name: clean });
    toast('File renamed');
    await loadEditorTree();
    if (editorOpenFile === filePath && data.destinationPath) await openEditorFile(data.destinationPath);
  }

  async function copyEditorTreeFile(filePath) {
    const destination = await chooseEditorDestination('copy', filePath);
    if (!destination) return;
    await runEditorSystemFileAction('copy', filePath, { to: destination });
    toast('File copied');
    await loadEditorTree();
  }

  async function moveEditorTreeFile(filePath) {
    const destination = await chooseEditorDestination('move', filePath);
    if (!destination) return;
    const data = await runEditorSystemFileAction('move', filePath, { to: destination });
    toast('File moved');
    await loadEditorTree();
    if (editorOpenFile === filePath) {
      if (data.destinationPath) await openEditorFile(data.destinationPath);
      else editorOpenFile = '';
    }
  }

  async function deleteEditorTreeFile(filePath) {
    if (!confirm(`Delete this file?\n\n${filePath}\n\nA backup copy will be saved in logs/deleted.`)) return;
    await runEditorSystemFileAction('delete', filePath, {});
    toast('File deleted');
    if (editorOpenFile === filePath) {
      editorOpenFile = '';
      editorLoadedText = '';
      const textarea = byId('editor-textarea');
      if (textarea) textarea.value = '';
    }
    await loadEditorTree();
    refreshEditorHeader();
  }

  async function clearEditorTreeFile(filePath) {
    if (!confirm(`Clear file contents?\n\n${filePath}`)) return;
    await runEditorSystemFileAction('clearfile', filePath, '');
    toast('File cleared');
    if (editorOpenFile === filePath) await openEditorFile(filePath);
    await loadEditorTree();
  }

  function buildAskLocalAITemplate() {
    const selectedCode = getCurrentEditorSelectionText();
    const lang = detectEditorLanguage(editorOpenFile) === 'script' ? 'ts' : detectEditorLanguage(editorOpenFile);
    const chat = byId('chat-input');
    if (!chat) return;

    const codeBlock = selectedCode
      ? `\n\n\`\`\`${lang || ''}\n${selectedCode}\n\`\`\`\n`
      : '\n\n```\n// Select a function or code snippet in the editor first.\n```\n';

    chat.value = [
      `Please help me make adjustments to the following code in filepath: ${editorOpenFile || '(no file selected)'}`,
      codeBlock.trimEnd(),
      '',
      'Describe your changes or request:',
      'Can you adjust the selected code so it does _________ and preserves the surrounding behavior?'
    ].join('\n');

    chat.dispatchEvent(new Event('input', { bubbles: true }));
    chat.focus();
    toast('Ask Local AI template added to chat');
  }

  function ensureEditorContextMenu() {
    let menu = byId('editor-context-menu');
    if (menu) return menu;
    menu = document.createElement('div');
    menu.id = 'editor-context-menu';
    menu.hidden = true;
    menu.innerHTML = [
      '<button type="button" class="editor-context-item" data-action="cut">Cut</button>',
      '<button type="button" class="editor-context-item" data-action="copy">Copy</button>',
      '<button type="button" class="editor-context-item" data-action="paste">Paste</button>',
      '<button type="button" class="editor-context-item" data-action="ask">Ask Local AI</button>',
      '<button type="button" class="editor-context-item" data-action="folder-new-file">New file here</button>',
      '<button type="button" class="editor-context-item" data-action="file-rename">Rename file</button>',
      '<button type="button" class="editor-context-item" data-action="file-copy">Copy file</button>',
      '<button type="button" class="editor-context-item" data-action="file-move">Move file</button>',
      '<button type="button" class="editor-context-item" data-action="file-clear">Clear file</button>',
      '<button type="button" class="editor-context-item" data-action="file-delete">Delete file</button>'
    ].join('');

    menu.addEventListener('click', async event => {
      const btn = event.target.closest('[data-action]');
      if (!btn || btn.disabled) return;
      const action = btn.dataset.action;
      closeEditorContextMenu();
      if (action === 'cut') await cutEditorSelection();
      if (action === 'copy') await copyEditorSelection(false);
      if (action === 'paste') await pasteIntoEditor();
      if (action === 'ask') buildAskLocalAITemplate();
      if (action === 'folder-new-file') await createEditorNewFileInFolder(editorContextFolderPath);
      if (action === 'file-rename') await renameEditorTreeFile(editorContextFilePath);
      if (action === 'file-copy') await copyEditorTreeFile(editorContextFilePath);
      if (action === 'file-move') await moveEditorTreeFile(editorContextFilePath);
      if (action === 'file-clear') await clearEditorTreeFile(editorContextFilePath);
      if (action === 'file-delete') await deleteEditorTreeFile(editorContextFilePath);
    });

    document.body.appendChild(menu);
    return menu;
  }

  function openEditorContextMenu(event, filePath = '', folderPath = '') {
    event.preventDefault();
    editorContextFilePath = String(filePath || '').trim();
    editorContextFolderPath = String(folderPath || '').trim();
    const menu = ensureEditorContextMenu();
    const cut = menu.querySelector('[data-action="cut"]');
    const copy = menu.querySelector('[data-action="copy"]');
    const paste = menu.querySelector('[data-action="paste"]');
    const ask = menu.querySelector('[data-action="ask"]');
    const newFile = menu.querySelector('[data-action="folder-new-file"]');
    const hasSelection = !!getCurrentEditorSelectionText();
    const hasFileContext = !!editorContextFilePath;
    const hasFolderContext = !!editorContextFolderPath && editorContextFolderPath !== '__editor_projects__';
    const folderOnlyContext = hasFolderContext && !hasFileContext;

    if (cut) cut.disabled = folderOnlyContext || editorCollapsedMode || !hasSelection;
    if (copy) copy.disabled = folderOnlyContext || (!hasSelection && !currentEditorText());
    if (paste) paste.disabled = folderOnlyContext || editorCollapsedMode;
    if (ask) ask.disabled = folderOnlyContext;
    if (newFile) newFile.disabled = !hasFolderContext;
    menu.querySelectorAll('[data-action^="file-"]').forEach(btn => { btn.disabled = !hasFileContext; });

    menu.hidden = false;
    menu.style.left = `${event.clientX}px`;
    menu.style.top = `${event.clientY}px`;
  }

  function closeEditorContextMenu() {
    const menu = byId('editor-context-menu');
    if (menu) menu.hidden = true;
  }

  function applyDefaultTreeCollapse(node, isProjectRoot) {
    editorCollapsedTree.clear();
    if (!node || !Array.isArray(node.children)) return;

    const collapseChildren = current => {
      if (!current || !Array.isArray(current.children)) return;
      for (const child of current.children) {
        if (child?.type === 'dir') {
          editorCollapsedTree.add(child.path);
          collapseChildren(child);
        }
      }
    };

    collapseChildren(node);
  }

  async function syncEditorProjectRoot(force = false) {
    const nextRoot = getEditorRootRequest();
    if (!force && nextRoot === editorLastRootRequest) return;
    editorLastRootRequest = nextRoot;
    editorOpenFile = '';
    editorLoadedText = '';
    const textarea = byId('editor-textarea');
    if (textarea) textarea.value = '';
    await loadEditorTree();
  }

  async function loadEditorTree() {
    if (typeof api !== 'function') { console.warn('[editor] api not ready, skipping loadEditorTree'); return; }

    const requestedRoot = getEditorRootRequest();
    const data = await api('/read/tree', 'POST', { root: requestedRoot });
    if (!data.ok || !data.root) {
      console.warn('[editor-tree] failed root request', requestedRoot, data);
      toast(`Unable to load file tree: ${requestedRoot}`);
      return;
    }
    editorRuntimeRootLabel = data.activeProject || (data.isBackstage ? 'Backstage' : 'workspace');
    editorTree = data.root;
    editorLastRootRequest = String(data.projectSerial || data.activeProject || requestedRoot || '');
    applyDefaultTreeCollapse(editorTree, true);
    renderEditorTree();
    refreshEditorHeader();
  }

  async function openEditorFile(filePath) {
    if (!isEditorEditableFile(filePath)) {
      showEditorNonEditableFile(filePath);
      return;
    }
    const data = await api('/read/file', 'POST', { path: filePath });
    const file = data.files?.[0];
    if (!data.ok || !file || !file.ok) {
      toast('Unable to open file');
      return;
    }

    editorOpenFile = filePath;
    editorLoadedText = file.content || '';
    editorCollapsedMode = false;
    editorCollapsedSelection = null;
    editorCollapsedAnchor = null;
    editorExpandedCollapsedBlock = null;
    const textarea = byId('editor-textarea');
    if (textarea) textarea.value = editorLoadedText;
    updateCollapsedBlocks();
    renderCollapsedView();
    setCollapsedMode(false);
    renderEditorTree();
    toggleEditorTreeDrawer(false);
    refreshEditorHeader();
    refreshSectionMenu();
  }

  async function createEditorNewFileInFolder(folderPath) {
    const folder = normalizeEditorPath(folderPath || '').replace(/[\\/]+$/, '');
    if (!folder || folder === '__editor_projects__') {
      toast('Choose a project folder first');
      return;
    }

    const rawName = prompt(`New file in:\n${folder}\n\nFile name:`);
    if (rawName == null) return;
    const cleanName = String(rawName || '').trim();
    if (!cleanName || cleanName.includes('/') || cleanName.includes('\\') || cleanName.split('/').includes('..')) {
      toast('Enter only a valid file name');
      return;
    }

    const filePath = joinEditorPathParts(folder, cleanName);
    if (!filePath) {
      toast('Enter a valid file name');
      return;
    }

    const manifest = {
      Task: 'editor create new file',
      Action: 'newfile',
      FilePath: filePath,
      Payload: '',
      Comment: 'Created from Verbatim editor folder context menu.'
    };

    const data = await api('/record', 'POST', manifest);
    if (!data.ok) {
      toast(data.message || data.error || 'New file failed');
      return;
    }

    toast(`Created ${filePath}`);
    await loadEditorTree();
    await openEditorFile(filePath);
    if (typeof refreshTape === 'function') refreshTape();
  }

  async function createEditorNewFile() {
    const rawPath = prompt('New file path under active project:');
    if (rawPath == null) return;

    const filePath = buildCanonicalEditorNewFilePath(rawPath);
    if (!filePath) {
      toast('Enter a valid relative file path');
      return;
    }

    const manifest = {
      Task: 'editor create new file',
      Action: 'newfile',
      FilePath: filePath,
      Payload: '',
      Comment: 'Created from Verbatim editor with lowercase canonical file path.'
    };

    const data = await api('/record', 'POST', manifest);
    if (!data.ok) {
      toast(data.message || data.error || 'New file failed');
      return;
    }

    toast(`Created ${filePath}`);
    await loadEditorTree();
    await openEditorFile(filePath);
    if (typeof refreshTape === 'function') refreshTape();
  }

  async function saveEditorFile() {
    if (!editorOpenFile) {
      toast('Open a file first');
      return;
    }

    const manifest = {
      Task: 'editor save current file',
      Action: 'rewritefile',
      FilePath: editorOpenFile,
      Payload: currentEditorText(),
      Comment: 'Saved from Verbatim editor.'
    };

    const data = await api('/record', 'POST', manifest);
    if (!data.ok) {
      toast('Save failed');
      return;
    }

    editorLoadedText = currentEditorText();
    updateCollapsedBlocks();
    renderCollapsedView();
    refreshEditorHeader();
    toast('Saved');
    if (typeof refreshTape === 'function') refreshTape();
  }

  function cycleEditorOutlineMode() {
    const currentIndex = EDITOR_OUTLINE_MODES.indexOf(editorOutlineMode);
    editorOutlineMode = EDITOR_OUTLINE_MODES[(currentIndex + 1) % EDITOR_OUTLINE_MODES.length];
    updateCollapsedBlocks();
    renderCollapsedView();
    if (!editorCollapsedBlocks.length) {
      toast(`Outline: ${EDITOR_OUTLINE_LABELS[editorOutlineMode]} (empty)`);
      return;
    }
    editorCollapsedAnchor = 0;
    editorCollapsedSelection = { start: 0, end: 0 };
    setCollapsedMode(true);
    const collapseBtn = byId('editor-collapse-btn');
    if (collapseBtn) collapseBtn.dataset.mode = editorOutlineMode;
    toast(`Outline: ${EDITOR_OUTLINE_LABELS[editorOutlineMode]}`);
  }

  function collapseEditorFunctions() {
    updateCollapsedBlocks();
    renderCollapsedView();
    if (!editorCollapsedBlocks.length) {
      toast('No top-level collapsible blocks found');
      return;
    }
    editorCollapsedAnchor = 0;
    editorCollapsedSelection = { start: 0, end: 0 };
    renderCollapsedView();
    setCollapsedMode(true);
  }

  function expandEditorFunctions() {
    editorCollapsedMode = false;
    const collapseBtn = byId('editor-collapse-btn');
    if (collapseBtn) delete collapseBtn.dataset.mode;
    setCollapsedMode(false);
    toast('Complete View');
  }

  function ensureEditorCorrectionsStyle() {
    document.getElementById('editor-corrections-style')?.remove();
  }

  function normalizeEditorLayout() {
    const root = byId('panel-editor');
    if (!root) return;
    root.classList.add('editor-compact-ready');
    return;

    root.querySelectorAll('button').forEach(btn => {
      btn.style.maxWidth = '100%';
      btn.style.flex = '0 0 auto';
      btn.style.minHeight = '30px';
      btn.style.whiteSpace = 'nowrap';
    });

    const actionParents = new Set();
    root.querySelectorAll('button').forEach(btn => {
      const parent = btn.parentElement;
      if (!parent) return;
      const buttonCount = parent.querySelectorAll(':scope > button').length;
      const inputCount = parent.querySelectorAll(':scope > input, :scope > textarea, :scope > select').length;
      if (buttonCount >= 2 || (buttonCount >= 1 && inputCount >= 1)) actionParents.add(parent);
    });

    actionParents.forEach(parent => {
      parent.style.display = 'flex';
      parent.style.flexWrap = 'wrap';
      parent.style.alignItems = 'center';
      parent.style.gap = '8px';
      parent.style.minWidth = '0';
      parent.querySelectorAll(':scope > input, :scope > textarea, :scope > select').forEach(field => {
        field.style.flex = '1 1 180px';
        field.style.minWidth = '0';
      });
    });
  }

  function expandAllTreeFolders() {
    editorCollapsedTree.clear();
    renderEditorTree();
  }

  function collapseAllTreeFolders() {
    if (editorTree) applyDefaultTreeCollapse(editorTree, false);
    renderEditorTree();
  }

  function jumpToEditorBlock(index) {
    const block = editorCollapsedBlocks[index];
    const textarea = byId('editor-textarea');
    if (!block || !textarea) return;
    setCollapsedMode(false);
    textarea.focus();
    textarea.setSelectionRange(block.startIndex, block.startIndex);
    syncEditorHighlight();
  }

  function navigateEditorBlock(direction) {
    if (!editorCollapsedBlocks.length) return;
    const current = editorCollapsedSelection?.start ?? 0;
    setEditorBlockSelection(current + direction, { jump: true });
  }

  function refreshSectionMenu() {
    const select = byId('editor-section-select');
    if (!select) return;
    select.innerHTML = '<option value="">Sections</option>';
    editorCollapsedBlocks.forEach((block, i) => {
      const opt = document.createElement('option');
      opt.value = String(i);
      opt.textContent = block.signature.slice(0, 80);
      select.appendChild(opt);
    });
    syncEditorSectionControl();
  }

  function toggleEditorTreeDrawer(force) {
    const root = byId('panel-editor');
    if (!root) return;
    const next = typeof force === 'boolean' ? force : !root.classList.contains('tree-open');
    root.classList.toggle('tree-open', next);
  }

  function toggleEditorFindPopover(force) {
    const popover = byId('editor-findbar');
    const trigger = byId('editor-find-toggle-btn');
    if (!popover) return;
    const next = typeof force === 'boolean' ? force : popover.hidden;
    popover.hidden = !next;
    if (next) {
      const rect = trigger?.getBoundingClientRect?.();
      const width = Math.min(560, Math.max(280, window.innerWidth - 96));
      const left = rect ? Math.min(rect.right + 8, window.innerWidth - width - 12) : 58;
      const top = rect ? Math.min(rect.top, window.innerHeight - 170) : 54;
      popover.style.width = `${width}px`;
      popover.style.left = `${Math.max(12, left)}px`;
      popover.style.top = `${Math.max(12, top)}px`;
      byId('editor-find-input')?.focus();
    }
  }

  function findInEditor() {
    const textarea = byId('editor-textarea');
    const query = String(byId('editor-find-input')?.value || '');
    if (!textarea || !query) return;
    const start = Math.max(textarea.selectionEnd || 0, 0);
    let index = textarea.value.indexOf(query, start);
    if (index < 0) index = textarea.value.indexOf(query, 0);
    if (index < 0) { toast('Find text not found'); return; }
    textarea.focus();
    textarea.setSelectionRange(index, index + query.length);
  }

  function replaceNextInEditor() {
    const textarea = byId('editor-textarea');
    const query = String(byId('editor-find-input')?.value || '');
    const replacement = String(byId('editor-replace-input')?.value || '');
    if (!textarea || !query) return;
    const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
    if (selected !== query) findInEditor();
    if (textarea.value.slice(textarea.selectionStart, textarea.selectionEnd) !== query) return;
    textarea.setRangeText(replacement, textarea.selectionStart, textarea.selectionEnd, 'end');
    refreshEditorHeader();
    updateCollapsedBlocks();
  }

  function replaceAllInEditor() {
    const textarea = byId('editor-textarea');
    const query = String(byId('editor-find-input')?.value || '');
    const replacement = String(byId('editor-replace-input')?.value || '');
    if (!textarea || !query) return;
    const next = textarea.value.split(query).join(replacement);
    if (next === textarea.value) { toast('Find text not found'); return; }
    textarea.value = next;
    refreshEditorHeader();
    updateCollapsedBlocks();
    toast('Replace all complete');
  }

  function bootEditor() {
    if (editorBooted) return;
    if (!byId('panel-editor')) return;
    editorBooted = true;

    ensureEditorCorrectionsStyle();
    normalizeEditorLayout();
    ensureEditorContextMenu();

    byId('editor-tree-toggle-btn')?.addEventListener('click', () => toggleEditorTreeDrawer());
    byId('editor-find-toggle-btn')?.addEventListener('click', () => toggleEditorFindPopover());
    byId('editor-refresh-btn')?.addEventListener('click', async () => { toggleEditorTreeDrawer(true); await loadEditorTree(); });
    byId('editor-save-btn')?.addEventListener('click', saveEditorFile);
    byId('editor-collapse-btn')?.addEventListener('click', cycleEditorOutlineMode);
    byId('editor-expand-btn')?.addEventListener('click', expandEditorFunctions);
    byId('editor-copy-btn')?.addEventListener('click', () => copyEditorSelection(editorCollapsedMode && !editorCollapsedSelection));
    byId('editor-find-next-btn')?.addEventListener('click', findInEditor);
    byId('editor-replace-next-btn')?.addEventListener('click', replaceNextInEditor);
    byId('editor-replace-all-btn')?.addEventListener('click', replaceAllInEditor);

    byId('editor-tree-expand-btn')?.addEventListener('click', expandAllTreeFolders);
    byId('editor-tree-collapse-btn')?.addEventListener('click', collapseAllTreeFolders);
    byId('editor-section-select')?.addEventListener('change', e => {
      const i = Number(e.target.value);
      if (!Number.isNaN(i)) setEditorBlockSelection(i, { jump: true });
    });
    byId('editor-prev-section')?.addEventListener('click', () => navigateEditorBlock(-1));
    byId('editor-next-section')?.addEventListener('click', () => navigateEditorBlock(1));

    byId('editor-textarea')?.addEventListener('input', () => {
      refreshEditorHeader();
      updateCollapsedBlocks();
      if (editorCollapsedMode) renderCollapsedView();
    });
    byId('editor-textarea')?.addEventListener('scroll', syncEditorHighlight);
    byId('editor-textarea')?.addEventListener('contextmenu', openEditorContextMenu);
    byId('editor-collapsed-view')?.addEventListener('contextmenu', openEditorContextMenu);
    byId('editor-textarea')?.addEventListener('keydown', event => {
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
        event.preventDefault();
        saveEditorFile();
        return;
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c' && editorCollapsedMode) {
        event.preventDefault();
        copyEditorSelection(false);
      }
    });

    document.addEventListener('click', event => {
      const menu = byId('editor-context-menu');
      if (!menu || menu.hidden) return;
      if (!menu.contains(event.target)) closeEditorContextMenu();
    });
    document.addEventListener('keydown', event => {
      if (event.key === 'Escape') {
        closeEditorContextMenu();
        toggleEditorFindPopover(false);
      }
    });
    window.addEventListener('blur', () => {
      closeEditorContextMenu();
      toggleEditorFindPopover(false);
    });

    document.querySelectorAll('.tab-btn[data-tab="editor"]').forEach(btn => {
      btn.addEventListener('click', () => {
        syncEditorProjectRoot(!editorTree);
        refreshEditorHeader();
      });
    });

    window.syncEditorProjectRoot = syncEditorProjectRoot;
    refreshEditorHeader();
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootEditor);
  else bootEditor();
})();