const { useState, useEffect, useRef, useMemo } = React;

// ── Slash commands ────────────────────────────────────
const SLASH_CMDS = [
  { label: 'Heading 1',       icon: 'H1',  desc: 'Large section heading',    insert: '# '                                                             },
  { label: 'Heading 2',       icon: 'H2',  desc: 'Medium section heading',   insert: '## '                                                            },
  { label: 'Heading 3',       icon: 'H3',  desc: 'Small section heading',    insert: '### '                                                           },
  { label: 'Blockquote',      icon: '❝',   desc: 'Quote or callout',         insert: '> '                                                             },
  { label: 'Bold',            icon: 'B',   desc: 'Bold text',                insert: '**bold**'                                                       },
  { label: 'Italic',          icon: 'I',   desc: 'Italic text',              insert: '*italic*'                                                       },
  { label: 'Wikilink',        icon: '[[',  desc: 'Link to another page',     insert: '[['                                                             },
  { label: 'Table',           icon: '⊞',   desc: 'Markdown table',           insert: '| Column 1 | Column 2 |\n|----------|----------|\n| Cell     | Cell     |' },
  { label: 'Divider',         icon: '—',   desc: 'Horizontal rule',          insert: '\n---\n'                                                        },
  { label: 'Code block',      icon: '{}',  desc: 'Code snippet',             insert: '```\n\n```'                                                     },
  { label: 'Mermaid diagram', icon: '~',   desc: 'Flowchart / Gantt / ER',   insert: '```mermaid\nflowchart TD\n    A[Start] --> B[End]\n```'          },
  { label: 'Infobox',         icon: 'ℹ',   desc: 'Wikipedia-style sidebar',  insert: ':::infobox\nname: \nType: \nStatus: \n:::'                       },
  { label: 'Image',           icon: '🖼',  desc: 'Inline image',             insert: '![alt text](url)'                                              },
];

function SlashMenu({ query, idx, style, onSelect, onClose }) {
  const filtered = SLASH_CMDS.filter(c => !query || c.label.toLowerCase().includes(query.toLowerCase())).slice(0, 10);
  useEffect(() => {
    const fn = e => {
      if (!['ArrowDown','ArrowUp','Enter','Escape','Tab'].includes(e.key)) return;
      e.preventDefault(); e.stopPropagation();
      if (e.key === 'Escape') { onClose(); return; }
      if (e.key === 'ArrowDown') { onSelect(null, 1); return; }
      if (e.key === 'ArrowUp')   { onSelect(null, -1); return; }
      if ((e.key === 'Enter' || e.key === 'Tab') && filtered[idx]) { onSelect(filtered[idx]); return; }
    };
    window.addEventListener('keydown', fn, true);
    return () => window.removeEventListener('keydown', fn, true);
  }, [filtered, idx, onSelect, onClose]);
  if (!filtered.length) return null;
  return (
    <div className="slash-menu" style={{ ...style, position: 'fixed' }}>
      {filtered.map((c, i) => (
        <div key={c.label} className={'slash-option' + (i === idx ? ' sel' : '')} onMouseDown={e => { e.preventDefault(); onSelect(c); }}>
          <div className="slash-icon">{c.icon}</div>
          <div><div className="slash-label">{c.label}</div><div className="slash-desc">{c.desc}</div></div>
        </div>
      ))}
    </div>
  );
}

// ── Wikilink dropdown ─────────────────────────────────
function WikilinkDropdown({ query, pages, idx, onSelect, onClose, style }) {
  const filtered = (pages || []).filter(p => { const q = (query || '').toLowerCase(); return p.title.toLowerCase().includes(q) || p.slug.includes(q); }).slice(0, 8);
  return (
    <div className="wikilink-dropdown" style={{ ...style, position: 'fixed' }}>
      {filtered.length > 0 ? filtered.map((p, i) => (
        <div key={p.slug} className={'wikilink-option' + (i === idx ? ' selected' : '')} onMouseDown={e => { e.preventDefault(); onSelect(p); }}>
          <div className="wikilink-option-title">{p.title}</div>
          <div className="wikilink-option-slug">{p.slug}</div>
        </div>
      )) : <div style={{ padding: '12px 14px', color: 'var(--text-muted)', fontSize: 13 }}>{query ? `No pages matching "${query}"` : 'Type to search…'}</div>}
    </div>
  );
}

// ── Block parser ──────────────────────────────────────
function parseBlocks(text) {
  if (!text.trim()) return [{ raw: '', type: 'para' }];
  const blocks = [], rx = /(:::infobox[\s\S]*?:::|```[\w]*\n[\s\S]*?```)/g;
  let last = 0, m;
  while ((m = rx.exec(text)) !== null) {
    if (m.index > last) text.slice(last, m.index).split(/\n{2,}/).forEach(t => { if (t.trim()) blocks.push({ raw: t.trim(), type: 'para' }); });
    blocks.push({ raw: m[0], type: m[0].startsWith(':::') ? 'infobox' : 'code' });
    last = m.index + m[0].length;
  }
  text.slice(last).split(/\n{2,}/).forEach(t => { if (t.trim()) blocks.push({ raw: t.trim(), type: 'para' }); });
  return blocks.length ? blocks : [{ raw: '', type: 'para' }];
}

function blockTaClass(seg) {
  const r = seg.raw;
  if (/^# /.test(r))   return 'seg-ta seg-ta-h1';
  if (/^## /.test(r))  return 'seg-ta seg-ta-h2';
  if (/^### /.test(r)) return 'seg-ta seg-ta-h3';
  if (/^>/.test(r))    return 'seg-ta seg-ta-bq';
  if (seg.type === 'code' || seg.type === 'infobox') return 'seg-ta seg-ta-code';
  return 'seg-ta';
}

// ── Single live block ─────────────────────────────────
function LiveBlock({ seg, isActive, pages, onActivate, onDeactivate, onUpdate, onAddAfter, onDelete, onNavigate }) {
  const taRef = useRef(null), renderedRef = useRef(null);
  const [wlSuggest, setWlSuggest]   = useState(null);
  const [wlIdx, setWlIdx]           = useState(0);
  const [slashMenu, setSlashMenu]   = useState(null);
  const [slashIdx, setSlashIdx]     = useState(0);

  useEffect(() => {
    if (isActive && taRef.current) {
      const ta = taRef.current;
      ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; ta.focus();
    }
    if (!isActive && renderedRef.current) window.runMermaid(renderedRef.current);
  }, [isActive]);

  function ar(ta) { ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; }

  function checkDD(ta) {
    const pos = ta.selectionStart, before = ta.value.slice(0, pos);
    const wlm = before.match(/\[\[([^\][\n]*)$/);
    if (wlm) {
      const r = ta.getBoundingClientRect(), lh = parseFloat(getComputedStyle(ta).lineHeight) || 28, ln = before.split('\n').length - 1;
      setWlSuggest({ query: wlm[1], style: { top: Math.min(r.top + (ln+1)*lh + 6, window.innerHeight - 280) + 'px', left: Math.min(r.left + 12, window.innerWidth - 320) + 'px' } });
      setWlIdx(0); setSlashMenu(null); return;
    }
    setWlSuggest(null);
    const sm = before.match(/(?:^|\n)\/([^\n]*)$/);
    if (sm) {
      const r = ta.getBoundingClientRect(), lh = parseFloat(getComputedStyle(ta).lineHeight) || 28, ln = before.split('\n').length - 1;
      setSlashMenu({ query: sm[1], style: { top: Math.min(r.top + (ln+1)*lh + 6, window.innerHeight - 340) + 'px', left: Math.min(r.left + 12, window.innerWidth - 320) + 'px' } });
      setSlashIdx(0); return;
    }
    setSlashMenu(null);
  }

  function insertWL(page) {
    const ta = taRef.current; if (!ta) return;
    const pos = ta.selectionStart, before = ta.value.slice(0, pos);
    const m = before.match(/\[\[([^\][\n]*)$/); if (!m) return;
    const start = pos - m[0].length, rep = `[[${page.title}]]`;
    const nv = ta.value.slice(0, start) + rep + ta.value.slice(pos);
    ta.value = nv; onUpdate(nv); setWlSuggest(null);
    setTimeout(() => { ta.focus(); ta.setSelectionRange(start + rep.length, start + rep.length); }, 0);
  }

  function applySlash(cmd) {
    if (!cmd) return;
    const ta = taRef.current; if (!ta) return;
    const pos = ta.selectionStart, before = ta.value.slice(0, pos);
    const m = before.match(/(?:^|\n)\/([^\n]*)$/); if (!m) return;
    const ss = pos - m[0].length + (m[0].startsWith('\n') ? 1 : 0);
    const nv = ta.value.slice(0, ss) + cmd.insert + ta.value.slice(pos);
    ta.value = nv; onUpdate(nv); setSlashMenu(null);
    setTimeout(() => { ta.focus(); ta.setSelectionRange(ss + cmd.insert.length, ss + cmd.insert.length); }, 0);
  }

  function handleKD(e) {
    if (wlSuggest) {
      const f = (pages || []).filter(p => { const q = (wlSuggest.query || '').toLowerCase(); return p.title.toLowerCase().includes(q) || p.slug.includes(q); }).slice(0, 8);
      if (e.key === 'ArrowDown') { e.preventDefault(); setWlIdx(i => Math.min(i+1, f.length-1)); return; }
      if (e.key === 'ArrowUp')   { e.preventDefault(); setWlIdx(i => Math.max(i-1, 0)); return; }
      if ((e.key === 'Enter' || e.key === 'Tab') && f[wlIdx]) { e.preventDefault(); insertWL(f[wlIdx]); return; }
      if (e.key === 'Escape') { e.preventDefault(); setWlSuggest(null); return; }
    }
    if (slashMenu && e.key === 'Escape') { setSlashMenu(null); return; }
    if (e.key === 'Backspace' && !wlSuggest && !slashMenu) {
      const ta = taRef.current;
      if (ta && ta.value.trim() === '') { e.preventDefault(); onDelete(); return; }
    }
    if (e.key === 'Enter' && e.shiftKey && !wlSuggest && !slashMenu) {
      e.preventDefault();
      const ta = taRef.current; if (ta) onUpdate(ta.value);
      onDeactivate(); onAddAfter(); return;
    }
    if (e.key === 'Tab' && !wlSuggest && !slashMenu) {
      e.preventDefault();
      const ta = taRef.current; if (!ta) return;
      const s = ta.selectionStart, nv = ta.value.slice(0, s) + '  ' + ta.value.slice(s);
      ta.value = nv; onUpdate(nv); setTimeout(() => ta.setSelectionRange(s+2, s+2), 0);
    }
  }

  async function handlePaste(e) {
    const items = e.clipboardData?.items; if (!items) return;
    for (const item of items) {
      if (item.type.startsWith('image/')) {
        e.preventDefault();
        const file = item.getAsFile(); if (!file) return;
        const token = localStorage.getItem('token');
        try {
          const form = new FormData(); form.append('file', file);
          const res = await fetch('/api/upload', { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: form });
          const data = await res.json();
          if (data.url) {
            const ta = taRef.current; if (!ta) return;
            const s = ta.selectionStart, ins = `![image](${data.url})`;
            const nv = ta.value.slice(0, s) + ins + ta.value.slice(s);
            ta.value = nv; onUpdate(nv); setTimeout(() => ar(ta), 0);
          }
        } catch {}
        return;
      }
    }
  }

  if (isActive) {
    return (
      <div className="block-wrap live-block-active" style={{ position: 'relative' }}>
        <textarea ref={taRef} className={blockTaClass(seg)} defaultValue={seg.raw}
          onInput={e => { ar(e.target); onUpdate(e.target.value); checkDD(e.target); }}
          onKeyDown={handleKD}
          onBlur={e => { onUpdate(e.target.value); setWlSuggest(null); setSlashMenu(null); onDeactivate(); }}
          onPaste={handlePaste} />
        {wlSuggest && <WikilinkDropdown query={wlSuggest.query} pages={pages} idx={wlIdx} onSelect={insertWL} onClose={() => setWlSuggest(null)} style={wlSuggest.style} />}
        {slashMenu && <SlashMenu query={slashMenu.query} idx={slashIdx} style={slashMenu.style}
          onSelect={(cmd, delta) => { if (cmd) applySlash(cmd); else setSlashIdx(i => { const f = SLASH_CMDS.filter(c => !slashMenu.query || c.label.toLowerCase().includes(slashMenu.query.toLowerCase())); return Math.max(0, Math.min(f.length-1, i + (delta||0))); }); }}
          onClose={() => setSlashMenu(null)} />}
      </div>
    );
  }

  const html = window.processMarkdown(seg.raw);
  return (
    <div ref={renderedRef} className="live-block"
      dangerouslySetInnerHTML={{ __html: html || '<span class="live-empty">Click to write…</span>' }}
      onClick={e => {
        const a = e.target.closest('a.wiki-link');
        if (a) { e.preventDefault(); onNavigate('#/wiki/' + a.dataset.slug); return; }
        onActivate();
      }} />
  );
}

// ── Editor ────────────────────────────────────────────
function Editor({ slug: initialSlug, user, onSave, onCancel, pages }) {
  const isNew = !initialSlug;
  const [title, setTitle]     = useState('');
  const [slugVal, setSlugVal] = useState(initialSlug || '');
  const [slugManual, setSlugManual] = useState(!!initialSlug);
  const [segs, setSegs]       = useState([{ raw: '', type: 'para' }]);
  const [tags, setTags]       = useState([]);
  const [aliases, setAliases] = useState([]);
  const [tagInput, setTagInput]     = useState('');
  const [aliasInput, setAliasInput] = useState('');
  const [isDraft, setIsDraft] = useState(false);
  const [activeIdx, setActiveIdx]   = useState(null);
  const [dragIdx, setDragIdx]       = useState(null);
  const [dropIdx, setDropIdx]       = useState(null);
  const [saving, setSaving]   = useState(false);
  const [error, setError]     = useState('');
  const [lastSaved, setLastSaved]   = useState(null);
  const fileInputRef = useRef(null);

  useEffect(() => {
    if (!initialSlug) { document.title = 'New Page — Wiki'; return; }
    const token = localStorage.getItem('token');
    fetch(`/api/pages/${initialSlug}`, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
      .then(async r => {
        if (!r.ok) return;
        const data = await r.json();
        const meta = data.meta || {};
        setTitle(meta.title || initialSlug.split('/').pop().replace(/-/g, ' '));
        if (meta.tags) setTags(Array.isArray(meta.tags) ? meta.tags : [meta.tags]);
        if (meta.aliases) setAliases(Array.isArray(meta.aliases) ? meta.aliases : [meta.aliases]);
        if (meta.draft) setIsDraft(meta.draft === 'true');
        setSegs(parseBlocks(data.content || ''));
        document.title = 'Editing — Wiki';
      });
  }, [initialSlug]);

  useEffect(() => {
    if (!slugManual && title) {
      setSlugVal(title.toLowerCase().trim().replace(/[^a-z0-9/]+/g, '-').replace(/^-+|-+$/g, ''));
    }
  }, [title, slugManual]);

  // Autosave
  useEffect(() => {
    const content = segs.map(s => s.raw).join('\n\n');
    const id = setInterval(() => {
      localStorage.setItem(`wiki_as_${initialSlug || 'new'}`, JSON.stringify({ title, content, tags, ts: Date.now() }));
      setLastSaved(new Date());
    }, 30000);
    return () => clearInterval(id);
  }, [title, segs, tags]);

  function updateSeg(i, raw) { setSegs(s => { const n = [...s]; n[i] = { ...n[i], raw }; return n; }); }
  function addSegAfter(i) {
    setSegs(s => { const n = [...s]; n.splice(i+1, 0, { raw: '', type: 'para' }); return n; });
    setTimeout(() => setActiveIdx(i+1), 20);
  }
  function deleteSeg(i) {
    setSegs(s => {
      if (s.length <= 1) return [{ raw: '', type: 'para' }];
      const n = [...s]; n.splice(i, 1); return n;
    });
    setActiveIdx(Math.max(0, i - 1));
  }
  function dropBlock(from, to) {
    if (from === to) return;
    setSegs(s => { const n = [...s]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
    setActiveIdx(null);
  }
  function addTag(v) { const t = v.trim().toLowerCase(); if (t && !tags.includes(t)) setTags([...tags, t]); setTagInput(''); }
  function addAlias(v) { const a = v.trim(); if (a && !aliases.includes(a)) setAliases([...aliases, a]); setAliasInput(''); }

  async function handleSave() {
    if (!slugVal.trim()) { setError('Slug is required'); return; }
    setSaving(true); setError('');
    const token = localStorage.getItem('token');
    const content = segs.map(s => s.raw).join('\n\n');
    const meta = {};
    if (title) meta.title = title;
    if (tags.length) meta.tags = JSON.stringify(tags);
    if (aliases.length) meta.aliases = JSON.stringify(aliases);
    if (isDraft) meta.draft = 'true';
    try {
      const res = await fetch(`/api/pages/${slugVal}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
        body: JSON.stringify({ content, meta })
      });
      if (!res.ok) throw new Error((await res.json()).error || 'Save failed');
      onSave(slugVal);
    } catch (e) { setError(e.message); }
    setSaving(false);
  }

  async function handleImageUpload(file) {
    if (!file) return null;
    const token = localStorage.getItem('token');
    const form = new FormData(); form.append('file', file);
    try {
      const res = await fetch('/api/upload', { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: form });
      const data = await res.json();
      return data.url || null;
    } catch { return null; }
  }

  const content = segs.map(s => s.raw).join('\n\n');
  const toc = window.extractTOC ? window.extractTOC(content) : [];

  return (
    <div className="article-wrap edit-mode">
      <div className="article-body">
        {/* Edit banner */}
        <div className="edit-banner">
          <span className="edit-banner-label">{isNew ? 'New page' : 'Editing'}</span>
          {lastSaved && <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>Autosaved {new Date(lastSaved).toLocaleTimeString()}</span>}
          <div style={{ display: 'flex', gap: 6, marginLeft: 'auto' }}>
            <button className="btn btn-ghost btn-sm" onClick={onCancel}>Cancel</button>
            <button className="btn btn-primary btn-sm" onClick={handleSave} disabled={saving}>{saving ? 'Saving…' : 'Save page'}</button>
          </div>
        </div>

        {error && <div style={{ background: 'oklch(20% 0.04 20)', border: '1px solid oklch(35% 0.10 20)', borderRadius: 'var(--radius)', padding: '8px 14px', fontSize: 13.5, color: 'oklch(65% 0.18 20)', marginBottom: 16 }}>{error}</div>}

        {/* Title + meta */}
        <div className="article-header">
          <input className="edit-title-input" type="text" value={title} onChange={e => setTitle(e.target.value)} placeholder="Page title" />
          <div className="article-meta" style={{ marginBottom: 8 }}>
            <div className="tags" style={{ alignItems: 'center', flex: 1 }}>
              {tags.map(t => <span key={t} className="tag">{t}<button className="tag-remove" onClick={() => setTags(tags.filter(x => x !== t))}>×</button></span>)}
              <input className="tag-add-input" placeholder="Add tag…" value={tagInput}
                onChange={e => setTagInput(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter' || e.key === ',') { e.preventDefault(); addTag(tagInput); } if (e.key === 'Backspace' && !tagInput && tags.length) setTags(tags.slice(0, -1)); }}
                onBlur={() => tagInput && addTag(tagInput)} />
            </div>
            <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12.5, color: 'var(--text-muted)', cursor: 'pointer' }}>
              <input type="checkbox" checked={isDraft} onChange={e => setIsDraft(e.target.checked)} style={{ accentColor: 'var(--accent)' }} />Draft
            </label>
          </div>
          {/* Aliases */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', paddingTop: 6, borderTop: '1px solid var(--border-soft)' }}>
            <span style={{ fontSize: 11.5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--text-muted)', flexShrink: 0 }}>Aliases</span>
            {aliases.map(a => <span key={a} className="tag" style={{ fontSize: 11.5 }}>{a}<button className="tag-remove" onClick={() => setAliases(aliases.filter(x => x !== a))}>×</button></span>)}
            <input className="tag-add-input" placeholder="Add alias…" value={aliasInput}
              onChange={e => setAliasInput(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter' || e.key === ',') { e.preventDefault(); addAlias(aliasInput); } if (e.key === 'Backspace' && !aliasInput && aliases.length) setAliases(aliases.slice(0, -1)); }}
              onBlur={() => aliasInput && addAlias(aliasInput)}
              style={{ fontSize: 12 }} />
          </div>
          {/* Slug */}
          <div style={{ marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--border-soft)' }}>
            <label style={{ fontSize: 11.5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.06em', color: 'var(--text-muted)', display: 'block', marginBottom: 4 }}>Path</label>
            <input type="text" value={slugVal} onChange={e => { setSlugVal(e.target.value); setSlugManual(true); }}
              style={{ width: '100%', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', padding: '5px 10px', fontSize: 13, fontFamily: 'var(--font-mono)', color: 'var(--text)' }} />
            <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 3 }}>Use / for nested pages (e.g. locations/city)</div>
          </div>
        </div>

        <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 12 }}>
          Click any section to edit · <kbd style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 3, padding: '0 4px', fontFamily: 'var(--font-mono)', fontSize: 10 }}>/</kbd> for commands · <kbd style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 3, padding: '0 4px', fontFamily: 'var(--font-mono)', fontSize: 10 }}>[[</kbd> to link · Shift+Enter for new block · drag ⠿ to reorder
        </div>

        {/* Live blocks */}
        <div className="markdown-body" style={{ display: 'block', paddingLeft: 28 }}>
          {segs.map((seg, i) => (
            <div key={i} className={'block-wrap' + (dragIdx === i ? ' block-dragging' : '')}
              onDragOver={e => { e.preventDefault(); setDropIdx(i); }}
              onDrop={e => { e.preventDefault(); if (dragIdx !== null) dropBlock(dragIdx, i); setDragIdx(null); setDropIdx(null); }}
              onDragEnd={() => { setDragIdx(null); setDropIdx(null); }}>
              {dropIdx === i && dragIdx !== null && dragIdx !== i && <div className="drop-line" />}
              <div className="drag-handle" draggable
                onDragStart={e => { e.dataTransfer.effectAllowed = 'move'; setDragIdx(i); setActiveIdx(null); }}
                title="Drag to reorder">⠿</div>
              <LiveBlock seg={seg} isActive={activeIdx === i} pages={pages || []}
                onActivate={() => setActiveIdx(i)}
                onDeactivate={() => setActiveIdx(null)}
                onUpdate={raw => updateSeg(i, raw)}
                onAddAfter={() => addSegAfter(i)}
                onDelete={() => deleteSeg(i)}
                onNavigate={slug => { onSave(slug); }} />
            </div>
          ))}
          <div style={{ minHeight: 60, cursor: 'text', paddingTop: 8 }}
            onClick={() => { setSegs(s => [...s, { raw: '', type: 'para' }]); setTimeout(() => setActiveIdx(segs.length), 20); }} />
        </div>

        {/* Image upload */}
        <input ref={fileInputRef} type="file" accept="image/*,video/*,.pdf" style={{ display: 'none' }}
          onChange={async e => {
            const f = e.target.files?.[0]; if (!f) return;
            const url = await handleImageUpload(f);
            if (url) {
              setSegs(s => [...s, { raw: `![${f.name}](${url})`, type: 'para' }]);
            }
            e.target.value = '';
          }} />
      </div>
      {toc.length > 1 && <window.TOC headings={toc} />}
    </div>
  );
}

Object.assign(window, { Editor });
