// content-blocks.jsx — рендер и редактирование блоков статьи

const CALLOUT_VARIANTS = {
  important: { label: 'Важно', icon: 'callout', className: 'callout--important' },
  attention: { label: 'Обратите внимание', icon: 'alertTriangle', className: 'callout--attention' },
  tip: { label: 'Совет', icon: 'check', className: 'callout--tip' },
  info: { label: null, icon: 'callout', className: 'callout--info' },
};
window.CALLOUT_VARIANTS = CALLOUT_VARIANTS;

window.readImageFile = (file, maxMb = 5) => new Promise((resolve, reject) => {
  if (!file || !file.type.startsWith('image/')) {
    reject(new Error('Выберите файл изображения'));
    return;
  }
  if (file.size > maxMb * 1024 * 1024) {
    reject(new Error(`Максимальный размер — ${maxMb} МБ`));
    return;
  }
  const reader = new FileReader();
  reader.onload = () => resolve(reader.result);
  reader.onerror = () => reject(new Error('Не удалось прочитать файл'));
  reader.readAsDataURL(file);
});

window.defaultBlocks = () => ([
  { id: Date.now(), type: 'h2', content: 'Зачем эта статья' },
  { id: Date.now() + 1, type: 'p', content: '' },
]);

window.parseBlocks = (article) => {
  if (article?.content?.blocks?.length) return article.content.blocks;
  return window.defaultBlocks();
};

const getLines = (text) => (text || '').split('\n');

const parseTodoItems = (block) => {
  if (block.items?.length) return block.items;
  return getLines(block.content).filter(Boolean).map((line, i) => {
    const trimmed = line.trim();
    const done = /^\[x\]/i.test(trimmed);
    const text = trimmed.replace(/^\[[ x]\]\s*/i, '');
    return { id: i, text, done };
  });
};

const todoToBlock = (items) => ({
  items,
  content: items.map((it) => `${it.done ? '[x]' : '[ ]'} ${it.text}`).join('\n'),
});

const parseTable = (content) => {
  const rows = getLines(content).filter(Boolean);
  if (!rows.length) return { headers: ['Колонка 1', 'Колонка 2'], body: [['', '']] };
  const headers = rows[0].split('|').map((c) => c.trim());
  const body = rows.slice(1).map((r) => {
    const cells = r.split('|').map((c) => c.trim());
    while (cells.length < headers.length) cells.push('');
    return cells.slice(0, headers.length);
  });
  return { headers, body };
};

const tableToContent = ({ headers, body }) => {
  const head = headers.join('|');
  const rows = body.map((r) => r.join('|'));
  return [head, ...rows].join('\n');
};

const parseVideoEmbed = (url) => {
  const u = (url || '').trim();
  if (!u) return null;

  const yt = u.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([\w-]{6,})/);
  if (yt) return { kind: 'iframe', src: `https://www.youtube.com/embed/${yt[1]}` };

  const vm = u.match(/vimeo\.com\/(?:video\/)?(\d+)/);
  if (vm) return { kind: 'iframe', src: `https://player.vimeo.com/video/${vm[1]}` };

  const rt = u.match(/rutube\.ru\/video\/([\w-]+)/);
  if (rt) return { kind: 'iframe', src: `https://rutube.ru/play/embed/${rt[1]}` };

  if (/\.(mp4|webm|ogg)(\?.*)?$/i.test(u) || u.startsWith('blob:') || u.startsWith('data:video')) {
    return { kind: 'video', src: u };
  }

  return null;
};

window.parseVideoEmbed = parseVideoEmbed;

// ── Редакторы блоков ──────────────────────────────────────────

const TodoBlockEditor = ({ block, onChange, onFocus }) => {
  const items = parseTodoItems(block);
  const updateItems = (next) => onChange({ ...block, ...todoToBlock(next) });

  const setItem = (id, patch) => updateItems(items.map((it) => (it.id === id ? { ...it, ...patch } : it)));
  const addItem = () => updateItems([...items, { id: Date.now(), text: '', done: false }]);
  const removeItem = (id) => updateItems(items.filter((it) => it.id !== id));

  return (
    <div className="block-todo-editor" onFocus={onFocus}>
      {items.map((it) => (
        <div key={it.id} className={`block-todo-row ${it.done ? 'done' : ''}`}>
          <button type="button" className={`block-todo-check ${it.done ? 'checked' : ''}`}
            onClick={() => setItem(it.id, { done: !it.done })} aria-label="Отметить">
            {it.done && <Icon name="check" size={11} />}
          </button>
          <input className="block-todo-input" value={it.text} placeholder="Текст задачи…"
            onChange={(e) => setItem(it.id, { text: e.target.value })} onFocus={onFocus} />
          <button type="button" className="block-line-remove" onClick={() => removeItem(it.id)} title="Удалить">
            <Icon name="x" size={12} />
          </button>
        </div>
      ))}
      <button type="button" className="block-add-line" onClick={addItem}>
        <Icon name="plus" size={12} /> Добавить задачу
      </button>
    </div>
  );
};

const ListBlockEditor = ({ block, ordered, onChange, onFocus }) => {
  const lines = getLines(block.content);
  const display = lines.length ? lines : [''];

  const updateLines = (next) => onChange({ ...block, content: next.join('\n') });
  const setLine = (i, val) => {
    const next = [...display];
    next[i] = val;
    updateLines(next);
  };
  const addLine = () => updateLines([...display, '']);
  const removeLine = (i) => updateLines(display.filter((_, idx) => idx !== i));

  return (
    <div className={`block-list-editor ${ordered ? 'ordered' : ''}`} onFocus={onFocus}>
      {display.map((line, i) => (
        <div key={i} className="block-list-row">
          <span className="block-list-marker">{ordered ? `${i + 1}.` : '•'}</span>
          <input className="block-list-input" value={line} placeholder="Пункт списка…"
            onChange={(e) => setLine(i, e.target.value)} onFocus={onFocus} />
          <button type="button" className="block-line-remove" onClick={() => removeLine(i)} title="Удалить">
            <Icon name="x" size={12} />
          </button>
        </div>
      ))}
      <button type="button" className="block-add-line" onClick={addLine}>
        <Icon name="plus" size={12} /> Добавить пункт
      </button>
    </div>
  );
};

const CodeBlockChrome = ({ langDisplay, lang, langOpen, langQuery, wrapRef, filtered, onToggleLang, onLangQuery, onPickLang, onCopy, copied, children }) => (
  <div className="code-block">
    <div className="code-block-chrome">
      <div className="code-block-dots" aria-hidden="true">
        <span /><span /><span />
      </div>
      <div className="code-block-lang-wrap" ref={wrapRef}>
        <button type="button" className="code-block-lang-btn" onClick={onToggleLang}>
          <Icon name="codeBlock" size={12} />
          <span>{langDisplay.label}</span>
          {langDisplay.mode === 'auto' && !langDisplay.detected && (
            <span className="code-block-auto-tag">авто</span>
          )}
          <Icon name="chevronDown" size={11} />
        </button>
        {langOpen && (
          <div className="code-block-lang-dropdown">
            <input
              className="code-block-lang-search"
              placeholder="Поиск языка…"
              value={langQuery}
              onChange={(e) => onLangQuery(e.target.value)}
              autoFocus
            />
            <div className="code-block-lang-list">
              {filtered.map((l) => (
                <button
                  type="button"
                  key={l.id}
                  className={`code-block-lang-item ${lang === l.id ? 'active' : ''}`}
                  onClick={() => onPickLang(l.id)}
                >
                  {l.name}
                  {lang === l.id && <Icon name="check" size={12} />}
                </button>
              ))}
              {!filtered.length && <div className="code-block-lang-empty">Язык не найден</div>}
            </div>
          </div>
        )}
      </div>
      <button type="button" className={`code-block-copy-btn ${copied ? 'copied' : ''}`} onClick={onCopy} title="Копировать код">
        <Icon name={copied ? 'check' : 'copy'} size={13} />
        <span>{copied ? 'Скопировано' : 'Копировать'}</span>
      </button>
    </div>
    {children}
  </div>
);

const CodeBlockEditor = ({ block, onChange, onFocus }) => {
  const [langOpen, setLangOpen] = React.useState(false);
  const [langQuery, setLangQuery] = React.useState('');
  const [html, setHtml] = React.useState('');
  const [copied, setCopied] = React.useState(false);
  const wrapRef = React.useRef(null);
  const textareaRef = React.useRef(null);
  const highlightRef = React.useRef(null);
  const lang = block.lang || 'auto';
  const content = block.content || '';
  const hasContent = Boolean(content.trim());
  const langDisplay = window.resolveCodeLangDisplay(lang, content);

  React.useEffect(() => {
    if (!langOpen) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setLangOpen(false);
    };
    const t = setTimeout(() => document.addEventListener('mousedown', onDoc), 0);
    return () => { clearTimeout(t); document.removeEventListener('mousedown', onDoc); };
  }, [langOpen]);

  React.useEffect(() => {
    let cancelled = false;
    if (!hasContent) {
      setHtml('');
      return undefined;
    }
    window.highlightCodeHtml(content, lang).then((result) => {
      if (!cancelled) setHtml(result || '');
    });
    return () => { cancelled = true; };
  }, [content, lang, hasContent]);

  const filtered = window.CODE_LANGUAGES.filter((l) => {
    const q = langQuery.trim().toLowerCase();
    if (!q) return true;
    return l.name.toLowerCase().includes(q) || l.id.toLowerCase().includes(q);
  });

  const syncScroll = () => {
    if (!highlightRef.current || !textareaRef.current) return;
    highlightRef.current.scrollTop = textareaRef.current.scrollTop;
    highlightRef.current.scrollLeft = textareaRef.current.scrollLeft;
  };

  const copyCode = async () => {
    try {
      await navigator.clipboard.writeText(content);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch {
      /* ignore */
    }
  };

  return (
    <CodeBlockChrome
      langDisplay={langDisplay}
      lang={lang}
      langOpen={langOpen}
      langQuery={langQuery}
      wrapRef={wrapRef}
      filtered={filtered}
      onToggleLang={() => setLangOpen((v) => !v)}
      onLangQuery={setLangQuery}
      onPickLang={(id) => {
        onChange({ ...block, lang: id, langManual: id !== 'auto' });
        setLangOpen(false);
        setLangQuery('');
      }}
      onCopy={copyCode}
      copied={copied}
    >
      <div className="code-block-body" onFocus={onFocus}>
        {!hasContent && (
          <div className="code-block-empty" aria-hidden="true">
            <Icon name="codeBlock" size={22} />
            <p>Вставьте код — язык определится автоматически</p>
          </div>
        )}
        {hasContent && (
          <pre ref={highlightRef} className="code-block-highlight hljs" aria-hidden="true">
            <code dangerouslySetInnerHTML={{ __html: html }} />
          </pre>
        )}
        <textarea
          ref={textareaRef}
          className={`code-block-input ${hasContent ? 'has-content' : ''}`}
          value={content}
          placeholder=""
          onChange={(e) => onChange({ ...block, content: e.target.value })}
          onScroll={syncScroll}
          onFocus={onFocus}
          spellCheck={false}
          rows={Math.max(6, content.split('\n').length + 1)}
        />
      </div>
    </CodeBlockChrome>
  );
};

const CodeBlockView = ({ block }) => {
  const [html, setHtml] = React.useState('');
  const [copied, setCopied] = React.useState(false);
  const lang = block.lang || 'auto';
  const content = block.content || '';
  const hasContent = Boolean(content.trim());
  const langDisplay = window.resolveCodeLangDisplay(lang, content);

  React.useEffect(() => {
    let cancelled = false;
    if (!hasContent) {
      setHtml('');
      return undefined;
    }
    window.highlightCodeHtml(content, lang).then((result) => {
      if (!cancelled) setHtml(result || '');
    });
    return () => { cancelled = true; };
  }, [content, lang, hasContent]);

  const copyCode = async () => {
    try {
      await navigator.clipboard.writeText(content);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch {
      /* ignore */
    }
  };

  if (!hasContent) {
    return (
      <div className="code-block code-block--view">
        <div className="code-block-chrome">
          <div className="code-block-dots" aria-hidden="true"><span /><span /><span /></div>
          <span className="code-block-lang-label">{langDisplay.label}</span>
        </div>
        <div className="code-block-body code-block-body--empty">
          <Icon name="codeBlock" size={22} />
          <p>Блок кода пуст</p>
        </div>
      </div>
    );
  }

  return (
    <div className="code-block code-block--view">
      <div className="code-block-chrome">
        <div className="code-block-dots" aria-hidden="true"><span /><span /><span /></div>
        <span className="code-block-lang-label">
          {langDisplay.label}
        </span>
        <button type="button" className={`code-block-copy-btn ${copied ? 'copied' : ''}`} onClick={copyCode} title="Копировать код">
          <Icon name={copied ? 'check' : 'copy'} size={13} />
          <span>{copied ? 'Скопировано' : 'Копировать'}</span>
        </button>
      </div>
      <pre className="code-block-view hljs"><code dangerouslySetInnerHTML={{ __html: html }} /></pre>
    </div>
  );
};

const QuoteBlockEditor = ({ block, onChange, onFocus }) => (
  <div className="block-quote-editor">
    <RichTextEditor
      className="block-quote-input rt-editor--quote"
      placeholder="Цитата или важная мысль…"
      value={block.content || ''}
      onChange={(html) => onChange({ ...block, content: html })}
      onFocus={onFocus}
      minHeight={56}
    />
  </div>
);

const CalloutBlockEditor = ({ block, onChange, onFocus }) => {
  const variant = block.variant || 'info';
  const cfg = CALLOUT_VARIANTS[variant] || CALLOUT_VARIANTS.info;
  const placeholders = {
    important: 'Укажите важную информацию…',
    attention: 'На что обратить внимание…',
    tip: 'Полезный совет…',
    info: 'Информационная заметка…',
  };

  return (
    <div className={`block-callout-editor block-callout-editor--${variant}`}>
      <div className="block-callout-variant-row">
        {Object.entries(CALLOUT_VARIANTS).map(([key, item]) => (
          <button
            key={key}
            type="button"
            className={`block-callout-variant-btn${variant === key ? ' active' : ''}`}
            onClick={() => onChange({ ...block, variant: key })}
          >
            {item.label || 'Заметка'}
          </button>
        ))}
      </div>
      <div className={`callout ${cfg.className}`}>
        <span className="callout-badge" aria-hidden="true">
          <Icon name={cfg.icon} size={14} />
        </span>
        <div className="callout-text">
          {cfg.label && <strong className="callout-label">{cfg.label}: </strong>}
          <RichTextEditor
            className="block-callout-input"
            placeholder={placeholders[variant]}
            value={block.content || ''}
            onChange={(html) => onChange({ ...block, content: html })}
            onFocus={onFocus}
            minHeight={40}
          />
        </div>
      </div>
    </div>
  );
};

const TableBlockEditor = ({ block, onChange, onFocus }) => {
  const table = parseTable(block.content);
  const update = (next) => onChange({ ...block, content: tableToContent(next) });

  const setHeader = (i, val) => {
    const headers = [...table.headers];
    headers[i] = val;
    update({ ...table, headers });
  };
  const setCell = (ri, ci, val) => {
    const body = table.body.map((row) => [...row]);
    if (!body[ri]) body[ri] = table.headers.map(() => '');
    body[ri][ci] = val;
    update({ ...table, body });
  };
  const addRow = () => update({ ...table, body: [...table.body, table.headers.map(() => '')] });
  const addCol = () => update({
    headers: [...table.headers, `Колонка ${table.headers.length + 1}`],
    body: table.body.map((row) => [...row, '']),
  });

  return (
    <div className="block-table-editor" onFocus={onFocus}>
      <table>
        <thead>
          <tr>{table.headers.map((h, i) => (
            <th key={i}>
              <input value={h} onChange={(e) => setHeader(i, e.target.value)} placeholder="Заголовок" />
            </th>
          ))}</tr>
        </thead>
        <tbody>
          {table.body.map((row, ri) => (
            <tr key={ri}>{row.map((cell, ci) => (
              <td key={ci}>
                <input value={cell} onChange={(e) => setCell(ri, ci, e.target.value)} placeholder="…" />
              </td>
            ))}</tr>
          ))}
        </tbody>
      </table>
      <div className="block-table-actions">
        <button type="button" className="block-add-line" onClick={addRow}><Icon name="plus" size={12} /> Строка</button>
        <button type="button" className="block-add-line" onClick={addCol}><Icon name="plus" size={12} /> Колонка</button>
      </div>
    </div>
  );
};

const ImageBlockEditor = ({ block, onChange, onFocus }) => {
  const inputRef = React.useRef(null);

  const pickFile = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    try {
      onChange({ ...block, src: await window.readImageFile(file) });
    } catch (err) { await window.UI.alert({ title: 'Ошибка', message: err.message }); }
    e.target.value = '';
  };

  const openPicker = () => inputRef.current?.click();

  return (
    <div className="block-img-editor" onFocus={onFocus}>
      {block.src ? (
        <div className="editor-img-preview-wrap">
          <img src={block.src} alt={block.content || ''} className="editor-img-preview" />
          <div className="editor-img-actions">
            <button type="button" className="btn btn-ghost btn-sm" onClick={openPicker}>Заменить</button>
            <button type="button" className="btn btn-ghost btn-sm" onClick={() => onChange({ ...block, src: null })}>Удалить</button>
          </div>
        </div>
      ) : (
        <div
          className="img-upload-zone"
          role="button"
          tabIndex={0}
          onClick={openPicker}
          onKeyDown={(e) => e.key === 'Enter' && openPicker()}
        >
          <Icon name="img" size={24} />
          <span>Нажмите, чтобы загрузить изображение</span>
          <span className="text-xs text-3">PNG, JPG до 5 МБ</span>
        </div>
      )}
      <input className="block-img-caption" placeholder="Подпись к изображению" value={block.content || ''}
        onChange={(e) => onChange({ ...block, content: e.target.value })} onFocus={onFocus} />
      <input ref={inputRef} type="file" accept="image/png,image/jpeg,image/webp,image/gif" hidden onChange={pickFile} />
    </div>
  );
};

const VideoBlockEditor = ({ block, onChange, onFocus }) => {
  const embed = parseVideoEmbed(block.src);

  return (
    <div className="block-video-editor" onFocus={onFocus}>
      <div className="block-video-url-row">
        <Icon name="video" size={16} className="block-video-icon" />
        <input
          className="block-video-url"
          placeholder="Ссылка на YouTube, Vimeo, Rutube или .mp4"
          value={block.src || ''}
          onChange={(e) => onChange({ ...block, src: e.target.value })}
          onFocus={onFocus}
        />
      </div>
      {embed ? (
        <div className="block-video-preview">
          {embed.kind === 'iframe' ? (
            <iframe src={embed.src} title={block.content || 'Видео'} allowFullScreen />
          ) : (
            <video src={embed.src} controls />
          )}
        </div>
      ) : block.src?.trim() ? (
        <div className="block-video-hint">Не удалось распознать ссылку. Поддерживаются YouTube, Vimeo, Rutube и прямые ссылки на mp4/webm.</div>
      ) : (
        <div className="block-video-placeholder">
          <Icon name="play" size={28} />
          <span>Вставьте ссылку на видео</span>
        </div>
      )}
      <input
        className="block-img-caption"
        placeholder="Подпись к видео"
        value={block.content || ''}
        onChange={(e) => onChange({ ...block, content: e.target.value })}
        onFocus={onFocus}
      />
    </div>
  );
};

// ── Просмотр ──────────────────────────────────────────────────

const renderView = (block, headingId) => {
  const headingWrap = (child) => (
    headingId ? <div id={headingId} className="article-heading-anchor">{child}</div> : child
  );
  switch (block.type) {
    case 'h1': return headingWrap(<RichHtmlView html={block.content} tag="h1" className="article-h1" mode="inline" />);
    case 'h2': return headingWrap(<RichHtmlView html={block.content} tag="h2" mode="inline" />);
    case 'h3': return headingWrap(<RichHtmlView html={block.content} tag="h3" mode="inline" />);
    case 'p': return <RichHtmlView html={block.content} tag="div" className="article-richtext-p" />;
    case 'ol': return <ol>{getLines(block.content).filter(Boolean).map((l, i) => <li key={i}>{l}</li>)}</ol>;
    case 'ul': return <ul>{getLines(block.content).filter(Boolean).map((l, i) => <li key={i}>{l}</li>)}</ul>;
    case 'todo': return (
      <div className="block-todo-view">
        {parseTodoItems(block).map((it) => (
          <div className={`task ${it.done ? 'done' : ''}`} key={it.id}>
            <div className="task-check">{it.done && <Icon name="check" size={11} />}</div>
            <div className="task-text">{it.text}</div>
          </div>
        ))}
      </div>
    );
    case 'quote': return (
      <blockquote className="article-quote">
        <RichHtmlView html={block.content} tag="div" />
      </blockquote>
    );
    case 'code': return <CodeBlockView block={block} />;
    case 'img': return block.src ? (
      <figure className="article-figure">
        <img src={block.src} alt={block.content || ''} className="article-img" />
        {block.content && <figcaption>{block.content}</figcaption>}
      </figure>
    ) : <div className="img-placeholder">{block.content || 'Изображение'}</div>;
    case 'video': {
      const embed = parseVideoEmbed(block.src);
      if (!embed) return block.src ? <div className="video-placeholder">Видео: {block.src}</div> : null;
      return (
        <figure className="article-figure article-video">
          <div className="article-video-wrap">
            {embed.kind === 'iframe' ? (
              <iframe src={embed.src} title={block.content || 'Видео'} allowFullScreen />
            ) : (
              <video src={embed.src} controls />
            )}
          </div>
          {block.content && <figcaption>{block.content}</figcaption>}
        </figure>
      );
    }
    case 'call': {
      const variant = block.variant || 'info';
      const cfg = CALLOUT_VARIANTS[variant] || CALLOUT_VARIANTS.info;
      return (
        <div className={`callout ${cfg.className}`}>
          <span className="callout-badge" aria-hidden="true">
            <Icon name={cfg.icon} size={14} />
          </span>
          <div className="callout-text">
            {cfg.label && <strong className="callout-label">{cfg.label}: </strong>}
            <RichHtmlView html={block.content} tag="span" className="callout-html" />
          </div>
        </div>
      );
    }
    case 'table': {
      const { headers, body } = parseTable(block.content);
      return (
        <table className="article-table-inline">
          <thead><tr>{headers.map((h, i) => <th key={i}>{h}</th>)}</tr></thead>
          <tbody>{body.map((row, ri) => (
            <tr key={ri}>{row.map((c, ci) => <td key={ci}>{c}</td>)}</tr>
          ))}</tbody>
        </table>
      );
    }
    case 'div': return <hr className="article-divider" />;
    default: return null;
  }
};

// ── Главный компонент ─────────────────────────────────────────

window.BlockContent = ({ block, editable, onChange, onFocus, headingId }) => {
  if (!editable) return renderView(block, headingId);

  switch (block.type) {
    case 'img': return <ImageBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'video': return <VideoBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'todo': return <TodoBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'ul': return <ListBlockEditor block={block} ordered={false} onChange={onChange} onFocus={onFocus} />;
    case 'ol': return <ListBlockEditor block={block} ordered onChange={onChange} onFocus={onFocus} />;
    case 'code': return <CodeBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'quote': return <QuoteBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'call': return <CalloutBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'table': return <TableBlockEditor block={block} onChange={onChange} onFocus={onFocus} />;
    case 'div': return <hr className="article-divider" />;
    case 'h1': return (
      <RichTextEditor
        className="editor-block-h1 rt-editor--heading"
        placeholder="Заголовок H1"
        value={block.content || ''}
        onChange={(html) => onChange({ ...block, content: html })}
        onFocus={onFocus}
        mode="inline"
      />
    );
    case 'h2': return (
      <RichTextEditor
        className="editor-block-h2 rt-editor--heading"
        placeholder="Заголовок H2"
        value={block.content || ''}
        onChange={(html) => onChange({ ...block, content: html })}
        onFocus={onFocus}
        mode="inline"
      />
    );
    case 'h3': return (
      <RichTextEditor
        className="editor-block-h3 rt-editor--heading"
        placeholder="Подзаголовок"
        value={block.content || ''}
        onChange={(html) => onChange({ ...block, content: html })}
        onFocus={onFocus}
        mode="inline"
      />
    );
    case 'p': return (
      <RichTextEditor
        className="editor-block-p"
        placeholder="Текст параграфа…"
        value={block.content || ''}
        onChange={(html) => onChange({ ...block, content: html })}
        onFocus={onFocus}
        minHeight={48}
      />
    );
    default: return null;
  }
};

window.blockTypeLabel = (type, variant) => {
  if (type === 'call' && variant && CALLOUT_VARIANTS[variant]?.label) {
    return CALLOUT_VARIANTS[variant].label;
  }
  return ({
    h1: 'H1', h2: 'H2', h3: 'H3', p: 'Текст', ul: 'Список', ol: 'Нумер.', todo: 'Задачи',
    quote: 'Цитата', code: 'Код', img: 'Фото', video: 'Видео', call: 'Заметка', table: 'Таблица', div: 'Линия',
  }[type] || type);
};

window.blockTypeIcon = (type) => ({
  h1: 'heading', h2: 'heading', h3: 'heading', p: 'text', ul: 'list', ol: 'listOl', todo: 'todo',
  quote: 'quote', code: 'codeBlock', img: 'img', video: 'video', call: 'callout', table: 'table', div: 'divider',
}[type] || 'file');
