ФреймворкJavaScript
Документация Blok
Руководства, полный справочник API и все встроенные блочные и строчные инструменты. Впервые здесь? Начните с быстрого старта — рабочий редактор займёт пять минут.
Начало работы
- Быстрый стартНачните работу с Blok за несколько простых шагов.
- Создайте первый редакторПодключите Blok, добавьте контент и сохраните его в JSON, который можно хранить и загружать обратно — полный цикл за пять шагов.
- Всё — это блокУ Blok одна основная идея. Поймите её — и остальной API сложится сам собой.
- Создание собственного блок-инструментаСоздайте блок-инструмент с нуля — блок-выноску, которая отрисовывается, редактируется и сохраняется как любой встроенный блок.
Основное
- Класс BlokОсновной класс редактора, который инициализирует экземпляр редактора Blok и управляет им. Любое пространство имён, доступное инструменту через `api.*`, доступно и на самом экземпляре как `editor.*` — свойства ниже описывают ту же поверхность плюс пространства имён `width`, `placeholder`, `tokens` и `i18n`, которые класс объявляет сам.
- КонфигурацияОбъект конфигурации, передаваемый в конструктор Blok. Формально он разделён на два типа: `BlokMountOptions` — опции, фиксированные на всё время жизни экземпляра (holder, tools, i18n, …) — и `BlokState`, ЖИВЫЕ поля: `readOnly` (включая `hideControls`), `hideToolbar`, `toolbarPosition`, `inlineToolbar`, а также колбэки редактора `onChange`, `onSave`, `onEnter`, `onSubmit`, `onBeforeRender` и `onAfterRender`. Каждому полю `BlokState` соответствует документированный рантайм-сеттер (`readOnly.set`, `toolbar.setHidden`, `toolbar.setPosition`, `tools.setInlineToolbar`, `handlers.set`), поэтому его изменение никогда не требует пересоздания редактора — и адаптеры React, Vue и Angular реагируют на эти props/inputs на месте. Само НАЛИЧИЕ колбэка тоже значимо (наличие `onSubmit` превращает Enter в «сериализовать и отправить», наличие `onSave` включает конвейер отслеживания изменений), поэтому `handlers.set` принимает и `undefined`, чтобы снять обработчик. `BlokConfig = BlokMountOptions & BlokState`, так что существующий код компилируется без изменений.
- БлокиУправление блоками в редакторе — создание, удаление, обновление и переупорядочивание контента.
- BlockAPIИнтерфейс для работы с отдельными блоками. Возвращается методами blocks.getById(), blocks.getBlockByIndex() и blocks.insert().
- СохранениеСохранение и экспорт содержимого редактора.
- View-рендерерПоказывайте сохранённые документы, не платя за редактор. Подпуть @bloklabs/core/view синхронно и без DOM превращает OutputData в семантический HTML или простой текст — он работает в Node, воркерах и React Server Components, — поэтому страницам «только для чтения» (публикации, превью, поисковая индексация, письма) больше не нужны экземпляр редактора, его бандл и асинхронное ожидание готовности. Каждое строчное поле санитизируется по составленному allowlist до подстановки, а политика URL-схем идентична редакторской; используйте функции вместе с defineBlokSchema — и документы отображаются под тем же составом санитизации, который их создал (если набор строчных инструментов меняется на лету через tools.setInlineToolbar, пересоберите схему, чтобы отображение не отстало). Для React очевидный путь «только для чтения» — <BlokView> (и useBlokView без обёртки): берите их вместо <BlokEditor readOnly>, который отгружает каждому читателю полный редакторский рантайм (панель инструментов, историю, механику мутаций). Вывод по умолчанию без стилей: подключите classes + root вместе с опциональным @bloklabs/core/view.css для паритета с редактором либо только toolAttributes с той же таблицей стилей — это бесклассовая база, воспроизводящая межблочные отступы редактора из тех же токенов --blok-block-padding-*; включите blockIds для глубоких ссылок «скопировать ссылку на блок» и передайте transformUrl, чтобы переписывать href и URL изображений в CDN.
Редактирование
- КурсорУправление позицией курсора и выделением внутри редактора.
- ВыделениеРабота с выделением текста внутри редактора.
- МеткиRange-aware операции со строчными метками для строчных инструментов форматирования. Там, где selection.findParentTag смотрит только на два граничных узла выделения (anchor и focus) и их предков, api.marks работает со ВСЕМ диапазоном: has отвечает «покрыт ли каждый текстовый узел выделения», apply и remove разрезают частично покрытые обёртки на границах диапазона, обновляют полностью покрывающие обёртки на месте и восстанавливают выделение, а apply и remove расширяют диапазон на замыкающий пробел, который браузеры исключают из выделения по двойному клику. Метка описывается декларативно через MarkSpec (tag, aliasTags, className, attributes, style); aliasTags позволяет унаследованным вариантам тега (например, <b> рядом со <strong>, <em> рядом с <i>) считаться ТОЙ ЖЕ меткой, при этом новые обёртки всегда используют канонический тег. Строковые значения статичны и входят в идентичность метки; значения-функции вычисляются из state, переданного в apply/toggle, и сознательно ИСКЛЮЧЕНЫ из идентичности — поэтому палитра цветов это ОДНА метка, обновляющаяся на месте, а не N взаимно отменяющих друг друга. Две спецификации с одинаковыми tag, classNames и статичными атрибутами принадлежат одной семье и компонуются на одном элементе — например, цвет текста и цвет фона на одном <mark>. Каждый метод по умолчанию берёт первый диапазон живого выделения. Экспорт ядра markSanitizerConfig(spec) выводит правило санитайзера для метки: тег попадает в allowlist, необъявленные style-свойства и классы вычищаются, объявленные атрибуты сохраняются, а значения-функции учитываются по имени свойства — динамические значения не теряются при сохранении. createReactInlineTool в React-адаптере применяет тот же вывод автоматически, когда инструмент объявляет спецификацию mark.
- СтилиДоступ к CSS-классам для стилизации пользовательских инструментов и элементов интерфейса, а также настройка макета и обрамления редактора через публичные CSS-переменные. Основной способ переопределить токены темы — `style.tokens` в конфигурации конструктора Blok: передайте ключи `--blok-*` со значениями, и Blok внедрит таблицу стилей для экземпляра, которая автоматически достаёт и до редактора, И до интерфейса, портированного в `document.body` (поповеры, подсказки, элементы верхнего слоя); некорректные ключи пропускаются с предупреждением, а таблица стилей удаляется при destroy. Внедрённые значения `style.tokens` статичны в рамках приложения — они применяются одинаково в светлой и тёмной темах и в режиме только для чтения, поэтому зависящим от состояния токенам вроде бокового отступа редактора (gutter) место в CSS; `style.tokens` игнорирует ключи `--blok-editor-gutter-*` с предупреждением. Но они не зафиксированы на момент создания: `editor.tokens.set(tokens)` переписывает внедрённую таблицу стилей на лету — именно это нужно переключателю светлой/тёмной темы на стороне хоста; без него смена токена означала бы пересоздание редактора или ручное написание глобальной таблицы стилей, нацеленной на области портала. `set()` принимает полный набор токенов (замена, а не слияние), повторяя семантику `style.tokens`, поэтому токены, отсутствующие в новой палитре, перестают действовать, а `{}` удаляет таблицу стилей; `editor.tokens.get()` возвращает то, что применено сейчас. API доступен синхронно сразу после создания (вызовы до `isReady` буферизуются и воспроизводятся), а адаптеры React/Vue/Angular управляют им реактивно — передайте `style={{ tokens }}` (React/Vue) или `[styleTokens]` (Angular), и изменения синхронизируются на месте без пересоздания редактора. Как альтернатива на чистом CSS: собственная палитра Blok объявлена с нулевой специфичностью через `:where()`, поэтому один обычный селектор вида `[data-blok-interface] { --blok-popover-bg: … }` выигрывает независимо от порядка таблиц стилей — но, так как поповеры портируются в `document.body`, эта глобальная таблица стилей должна дополнительно нацеливаться на `[data-blok-popover], [data-blok-top-layer]`, чтобы достать до них. `--blok-content-max-width` остаётся определяющим в обоих режимах ширины — `width='full'` лишь подменяет его запасное значение на `none`. В режиме редактирования Blok автоматически резервирует 56px бокового отступа под плавающие элементы управления блоком +/⠿, а на обёртке появляется `data-blok-readonly`, пока активен режим только для чтения. Обычный режим только для чтения СОХРАНЯЕТ этот отступ: в нём живёт элемент «скопировать ссылку», появляющийся при наведении на блок, а `readOnly.set()` переключает режимы на месте — схлопывание отступа сдвигало бы документ вбок при каждом переключении. Отступ автоматически схлопывается до 0 только там, где он действительно мёртвое пространство: в режиме только для чтения без элементов управления (`readOnly: { hideControls: true }`, обёртка получает `data-blok-controls-hidden`) и при `hideToolbar: true` в конфигурации конструктора — панель инструментов при наведении тогда не открывается, обёртка получает `data-blok-toolbar-hidden`, и место под отступ не резервируется. `--blok-editor-gutter-start` — это точка переопределения, а не обязательное заклинание: задайте ему любое значение (в том числе `0px`, чтобы убрать отступ), чтобы изменить значение по умолчанию. Контракт переопределения отступа гарантирован, а не случаен: Blok объявляет и значение по умолчанию, и оба схлопывания по состоянию с нулевой специфичностью через `:where()` (это закреплено контрактным юнит-тестом), поэтому объявление токенов отступа на стороне хоста с любой положительной специфичностью всегда выигрывает каскад. Объявляйте их на самом элементе-обёртке (например, `[data-blok-interface] { --blok-editor-gutter-start: 16px }`), а не только на предке: схлопывания controls-hidden и toolbar-hidden переобъявляют эти токены на обёртке, а пользовательские свойства разрешаются из ближайшего объявления, поэтому значение на уровне предка проиграет схлопыванию, а значение на уровне обёртки его переживёт. Горизонтальное положение колонки контента тоже настраивается на уровне API — через `style.contentAlign?: 'left' | 'center' | 'right'` (по умолчанию `'left'`) в конфигурации конструктора Blok. Blok также перекрашивает нативное выделение текста внутри редактора через `--blok-selection-inline` — переопределите этот токен, чтобы изменить цвет, или передайте `style.nativeSelection: true` (по умолчанию `false`), чтобы полностью отказаться от этого и вернуться к цветам выделения браузера/хоста (переопределение токена не может выразить общие CSS-ключевые слова вроде `revert`, поэтому для возврата нужен именно этот флаг). С включённым флагом обёртка получает `data-blok-native-selection`, правила `::selection` от Blok обходят редактор, а подсветка fake-background (показываемая, пока фокус удерживает поле ввода в меню) следует цвету `Highlight` из браузера; в поповерах цвет выделения от Blok сохраняется. Фоновые поверхности — тоже публичные токены: большинство светлых поверхностей и поверхностей при наведении следуют `--blok-bg-light`, карточки пустого состояния медиа используют `--blok-bg-secondary` (с границей `--blok-border-secondary`), а скелетоны загрузки изображений/файлов и плейсхолдеры загрузки — `--blok-bg-tertiary`, который по умолчанию равен `--blok-bg-light` и потому следует за темой; перекрасить поверхность скелетона — значит переопределить `--blok-bg-tertiary` напрямую, а не перегружать `--blok-bg-light`, утягивая за собой все остальные поверхности. Как и все цветовые токены палитры, токены поверхностей переобъявляются самим Blok на обёртке редактора с нулевой специфичностью, поэтому применяйте переопределения через `style.tokens` / `editor.tokens.set()` либо через CSS-селектор, попадающий в саму обёртку (`[data-blok-interface]`): объявление пользовательского свойства на контейнере-предке перекрывается собственным объявлением обёртки и молча ничего не делает (компоновочные хуки вроде `--blok-content-max-width`, а также токены списков, заголовков, embed, отступов блока и цвета плейсхолдера, наоборот, читаются с запасными значениями и никогда не объявляются самим Blok — поэтому они ДЕЙСТВИТЕЛЬНО наследуются от любого предка; токены бокового отступа и `--blok-search-input-placeholder` объявляются на обёртке, как палитра, поэтому им тоже нужно правило уровня обёртки). Учтите также, что внедряемые таблицы токенов нацелены на атрибуты области Blok глобально: если на странице несколько экземпляров редактора, таблица стилей `style.tokens` / `tokens.set()` каждого экземпляра применяется ко ВСЕМУ интерфейсу Blok на странице, а не только к своему экземпляру (каждая удаляется при уничтожении своего экземпляра; при конфликте наборов между экземплярами решает порядок таблиц стилей в `<head>`, а не давность применения, поэтому давайте всем экземплярам один общий набор вместо расчёта на порядок конфликта) — различия между экземплярами задавайте CSS-правилом на собственной обёртке каждого редактора (интерфейс поповеров, смонтированный в body, всегда следует общестраничным таблицам). Таблицы внедряются в начало `<head>`, поэтому правило таблицы стилей хоста с равной специфичностью — обычное `[data-blok-interface] { … }` — всё равно побеждает `style.tokens` для объявленных в нём токенов. Ритм блоков тоже публичен: `--blok-block-padding-top`, `--blok-block-padding-bottom` и `--blok-block-padding-inline` задают внутренние отступы обёртки любого блочного инструмента (параграф, заголовок, список, тоггл, цитата). Каждый инструмент сохраняет своё историческое значение как запасное — 7px/7px/2px у большинства блоков, 0.2em по вертикали у цитат — поэтому одно переопределение перенастраивает все блоки сразу; именно это нужно хосту в режиме только для чтения для плотной строчной вёрстки (раньше это было возможно только переопределением внутренностей `[data-blok-tool]`). Панель выноски — намеренное исключение: внутренний отступ её карточки задаётся `--blok-callout-padding-block` (по умолчанию 5px), а НЕ токенами ритма, поэтому уплотнение ритма не может схлопнуть карточку выноски на её текст — при этом эмодзи остаётся на первой строке текста, потому что его кнопка следует за `--blok-block-padding-top` вместе с дочерним текстом. Учтите, что нестандартные отступы слегка сдвигают производную геометрию — например, смещение стрелки заголовка-тоггла, которое следует за `--blok-block-padding-top`. Раскладка колонок публична точно так же: строка колонок — это `[data-blok-columns]`, а холдер каждой колонки — один из её непосредственных дочерних `[data-blok-element]` (строка в режиме только для чтения дополнительно несёт `data-blok-columns-static-gutter`, поскольку в опубликованных строках зазор задаёт контейнер, а не разделители `[data-blok-column-resizer]`, которые существуют только во время редактирования). `--blok-column-gutter` задаёт этот зазор (по умолчанию `min(2rem, 4vw)`), а `--blok-column-min-width` — насколько сильно можно сжать колонку (по умолчанию `0`, то есть колонку можно перетащить до полного схлопывания). Нижний предел соблюдают И раскладка, И перетаскивание разделителя — при нажатии указателя перетаскивание считывает вычисленное значение обратно, — поэтому его повышение останавливает ручку на этом пределе, а не сохраняет ширину, которую раскладка откажется отрисовывать. Вложенность блоков публична так же: блок, вложенный в другой (Tab на верхнем уровне), получает отступ `--blok-block-indent-step` на каждый уровень (по умолчанию `24px`), и это настоящий CSS, а не инлайновый стиль, поэтому обычное правило хоста меняет или убирает его без `!important`. Blok обнуляет шаг внутри каждого дочернего слота `[data-blok-nested-blocks]` — этот маркер рендерит для своих детей любой контейнерный инструмент, встроенный и сторонний, — поэтому блоки, которые контейнер уже расположил сам, не сдвигаются дополнительно по своей глубине; контейнеру, которому отступ нужен, достаточно объявить шаг обратно на своём слоте. Этот сброс держится на наследовании, а не на проверке в JS, именно затем, чтобы он действовал и для слота, созданного уже после вставки дочернего блока, — а именно так поступает портал адаптера фреймворка. Размер текста публичен и для блока, И для сценария через `style.fontSize` — это поддерживаемая альтернатива обращению к внутренним именам классов Blok. Каждый ключ пишет один публичный токен: `fontSize.paragraph` → `--blok-paragraph-font-size`, `fontSize.heading[1]` → `--blok-heading-1-font-size` (заголовки переиспользуют уже существующие токены заголовков, а не заводят параллельные), `fontSize.list.checklist` → `--blok-checklist-font-size`, и так же для обоих вариантов цитаты, выноски, кода, тоггла, двух плотностей таблицы (`compact` / `comfortable`), каждой подписи к медиа (изображение, видео, аудио, файл, embed) и трёх частей закладки (заголовок, описание, ссылка). Опущенные ключи сохраняют встроенный размер Blok, поэтому везде, где вы не подключились явно, редактор отображается ровно как раньше. Настройки размера на уровне инструмента по-прежнему главнее: инструмент параграфа с `styles.size` или список с `itemSize` пишет этот размер инлайновым стилем на блоке, и его не переопределить никаким токеном — поэтому сценарии, которыми вы хотите управлять через `style.fontSize`, не должны одновременно нести размер на уровне инструмента. Значения могут быть абсолютными или относительными (`px`, `rem`, `em`, `%`): каждый декоративный элемент рядом с текстом заданного размера — маркер списка, чекбокс, эмодзи выноски, стрелка тоггла — выводит собственные метрики из того же токена, поэтому остаётся оптически выровненным при любом масштабе без дополнительного CSS. Так как эти токены читаются с запасными значениями и никогда не объявляются самим Blok, редактор, который НЕ настраивает `style.fontSize`, принимает их и из обычного CSS-правила на любом предке, и из `style.tokens` / `editor.tokens.set()`. Сами ИМЕНА токенов поставляются константой — `import { BLOK_FONT_SIZE_TOKENS } from '@dodopizza/blok'` даёт карту ровно той же формы, что и конфигурация (`BLOK_FONT_SIZE_TOKENS.paragraph`, `BLOK_FONT_SIZE_TOKENS.heading[1]`, `BLOK_FONT_SIZE_TOKENS.bookmark.link`…), поэтому хосту, который задаёт типографику из CSS, никогда не приходится переписывать эти строки вручную, а переименование становится ошибкой компиляции, а не молчаливым no-op. Каналы дополняют друг друга: `style.fontSize` — это значение на момент создания, а `editor.tokens.set({ [BLOK_FONT_SIZE_TOKENS.paragraph]: '18px' })` переопределяет его на лету — таблица токенов темы внедряется сразу после таблицы fontSize при равной специфичности, поэтому она выигрывает. Именно этот канал нужен размеру, который должен меняться после монтирования (переключатель плотности, масштаба или доступности); сам `style.fontSize` читается один раз при создании. В отличие от `style.tokens`, внедряемая таблица fontSize ограничена своим редактором: обёртка несёт `data-blok-instance`, и селектор редактора в этой таблице привязан к нему, поэтому второй редактор на странице сохраняет встроенные размеры Blok (или собственную конфигурацию), а не наследует размеры первого. Общестраничным остаётся лишь интерфейс, смонтированный в body, — поповеры и подсказки отрисовываются вне поддерева любого редактора, поэтому при расхождении экземпляров эти правила следуют порядку в `<head>`. Стоит знать одно правило вложенности: выноска отображает свой текст как дочерний блок-параграф, поэтому текст выноски следует `fontSize.callout` и откатывается к `fontSize.paragraph`, когда этот ключ не задан — задав только `paragraph`, вы измените размер тела выносок вместе с основным текстом, а чтобы они различались, нужно задать `fontSize.callout` явно. Наконец, view-рендерер (`@bloklabs/core/view`) выдаёт семантический HTML, и его таблица стилей несёт только сценарии, основанные на классах: в выводе view работают параграф, заголовки, список, чек-лист, оба размера цитаты, выноска, код и тоггл, а размеры подписей, ячеек таблицы и закладок доступны только в редакторе.
- ИсторияУправление функциональностью отмены/повтора для операций редактора.
Интерфейс
- Панель инструментовУправление панелью инструментов блока и её состоянием.
- Строчная панельУправление строчной панелью форматирования (жирный, курсив и т.д.).
- ИнтерфейсДоступ к UI-элементам и состоянию Blok.
- УведомленияОтображение уведомлений для пользователей. Отрисовка подключаемая: передайте `notifier: (options) => …` в конфигурации конструктора, и Blok вызовет ваш обработчик вместо собственной отрисовки — встроенный тост пропускается полностью (вместе со значениями `okText`/`cancelText` из i18n), а любая ошибка вашего обработчика доходит до места вызова `show()`. `notifierPosition` задаёт положение встроенного контейнера: 'bottom-left' | 'bottom-right' | 'bottom-center' | 'top-left' | 'top-right' | 'top-center' (по умолчанию 'bottom-center').
- Всплывающие подсказкиОтображение всплывающих подсказок на элементах интерфейса.
- ТемаЧтение и смена цветовой темы редактора на лету. Заданный режим и реально отрисованная тема — два разных вопроса: `get()` отвечает на первый, `getResolved()` — на второй. Чтобы получать уведомления вместо опроса, передайте опцию конфигурации ядра `onThemeChange`: она срабатывает с разрешённой темой при её изменении (включая смену системной настройки, пока режим равен 'auto').
- ШиринаУправление режимом ширины контента редактора: 'narrow' удерживает содержимое внутри `--max-width-content` по умолчанию, 'full' снимает ограничение, и содержимое заполняет свой контейнер.
- ПлейсхолдерЧтение и смена плейсхолдера уровня редактора (подсказки в пустом блоке инструмента по умолчанию) на лету, без пересоздания редактора.
Расширение и система
- ИнструментыДоступ и управление инструментами редактора.
- ЗагрузчикЗагрузить ассет через конвейер, который отвечает за его ТИП, а не через тот инструмент, который случайно оказался запрашивающим. Инструменты вызывают именно этот метод, а не лезут в собственный `config.uploader`, — поэтому обложка аудиоблока попадает в ваш конвейер изображений, а не в аудиоэндпоинт, который её отверг бы. Порядок разрешения для типа: инструмент, у которого совпадает статический `assetKind` (например, `tools.image.config.uploader` для `'image'`), затем конфигурация `uploader` уровня редактора, затем локальный запасной вариант — `blob:` URL для файлов и URL как есть для ссылок. Готовые реализации `uploader` — Supabase, S3-совместимое хранилище, Cloudinary и IndexedDB, — которым не нужен собственный бэкенд, смотрите на странице пресетов хранилищ.
- СобытияПодписка и управление событиями жизненного цикла редактора.
- СлушателиУправление пользовательскими обработчиками DOM-событий с автоматической очисткой.
- ОчисткаОчистка HTML-контента для защиты от XSS-атак. В `static get sanitize()` инструмента поле данных можно сопоставить со строкой `'plaintext'` вместо карты тегов — это помечает поле как литеральный исходный текст, а не разметку.
- Только чтениеУправление режимом только для чтения. Переключение происходит на месте, пока каждый зарегистрированный блочный инструмент реализует `setReadOnly(state)` в своём прототипе — все встроенные инструменты это делают, — поэтому тот же экземпляр редактора меняет режим, сохраняя позицию курсора, историю отмен и прокрутку, а переключатель «редактирование/просмотр» — это `readOnly.set(!isEditing)` на ОДНОМ экземпляре вместо уничтожения одного редактора и создания другого. Проверка работает по принципу «всё или ничего»: достаточно установить один блочный инструмент без `setReadOnly`, и КАЖДОЕ переключение уходит на запасной путь save → clear → повторный рендер, который пересоздаёт все экземпляры блоков и не восстанавливает курсор (прокрутка при этом восстанавливается, а история отмен намеренно остаётся нетронутой).
- ЛокализацияПоддержка интернационализации для перевода строк интерфейса, а также рантайм-мутатор `i18n.update()`, который меняет язык на месте. Сам каталог локалей поставляется отдельной опубликованной точкой входа `@bloklabs/core/locales`: в бандл входит только английский, остальные 68 локалей загружаются по требованию, а `normalizeLocale()` — предварительная проверка для локали, которую вы не задали жёстко: `i18n.update({ locale })` с неподдерживаемым тегом сохраняет текущую локаль и выводит предупреждение в консоль вместо исключения.
- Подмена версии для разработкиМеханизм для разработки, который есть в каждой опубликованной сборке: как он работает, почему это безопасно и как убрать его из своего бандла.
Типы данных
- Выходные данныеСтруктура данных, возвращаемая методом save(). Во входных позициях — опция конфигурации `data`, `render()`, `blocks.render()` и `blocks.insertMany()` — также принимаются нестрогие варианты `LooseOutputData` / `LooseOutputBlockData`, где `data`, `id`, `parent`, `content` и `time` блока могут быть `null`: `null` в `data` становится `{}`, `null`/пустой `id` заменяется сгенерированным, а `null` в `parent` и `null`/пустой массив в `content` считаются отсутствующими (блок корневой и без детей). Сохранённый вывод всегда имеет строгую форму.
- Данные блокаСтруктура каждого блока в массиве blocks.
Адаптеры фреймворков
- Компонент BlokEditorКомпонент-редактор «всё в одном», который поставляют адаптеры фреймворков: <BlokEditor> в @bloklabs/react и @bloklabs/vue, <blok-editor> (BlokEditorComponent) в @bloklabs/angular. React и Vue принимают любую опцию конфигурации редактора как проп и пробрасывают неизвестные пропы/атрибуты на контейнерный div. Angular устроен иначе: он объявляет ограниченный набор `@Input()` — tools, data, readOnly, hideToolbar, toolbarPosition, inlineToolbar, theme, width, placeholder, styleTokens, i18n, autofocus, migrations, onBeforeRender, onBeforePaste, onError — плюс запасной вход `[config]` для всех остальных ключей конфигурации (sanitizer, minHeight, defaultBlock, dataModel, link, linkPaste, tunes, user, resolveUser, uploader, server, ticket, persistence, collaboration, notifier, logLevel, onEnter, onSubmit, scrollToBlock, …) и не пробрасывает атрибуты хоста на контейнерный div. Живой экземпляр Blok читается через ref/onReady (React), через `instance` на шаблонной ссылке или эмит `@ready` (Vue) и через сигнал `instance` или output `(ready)` (Angular). Пропы ниже описывают специфичную для адаптеров поверхность; остальное совпадает с опциями раздела «Конфигурация».
- useBlocksРеактивный снимок дерева блоков плюс полный API манипуляций от адаптеров фреймворков: хук useBlocks(editor, options?) в @bloklabs/react, composable useBlocks(editor, options?) в @bloklabs/vue и `injectBlocks(editor, options?)` в @bloklabs/angular — передайте сигнал `instance` у BlokEditorComponent/BlokContentDirective и вызывайте из контекста внедрения (инициализатора поля или конструктора). Чтения реактивно перерисовываются при изменении документа; записи атомарны (один шаг отмены) и безопасны до готовности редактора (превращаются в no-op). Возвращаемые объекты BlockNode ({ id, type, parentId, contentIds }) — волатильные свежие снимки: читайте их сразу, не сохраняйте в массивах зависимостей. По умолчанию реактивность охватывает весь документ: передайте `{ within: blockId }`, чтобы перерисовка происходила только при изменениях внутри поддерева этого блока (самого блока или любого его потомка). Используйте это в контейнерном блоке, который отрисовывает только собственных детей, — без ограничения области такой блок перерисовывается на каждое нажатие клавиши в любом месте документа, а страница из N контейнеров превращает одно нажатие в N перерисовок. Область ограничивает перерисовки, а не чтения: даже с заданной областью вы по-прежнему видите всё дерево, поэтому getById/getChildren продолжают работать с чем угодно. Во Vue область принимает ещё и ref/getter, а в Angular — signal, и значение читается в момент отправки изменения, поэтому его смена не требует переподписки.
- useBlokReadyЖивая готовность редакторов Blok внутри поддерева DOM — булево значение, от которого можно рендерить: хук useBlokReady(options) в @bloklabs/react, composable useBlokReady(options) в @bloklabs/vue (возвращает ref) и injectBlokReady(options) в @bloklabs/angular (возвращает signal). Все три обёртки используют один и тот же реестр ядра за Blok.readyState() и Blok.subscribeReady(), поэтому разойтись не могут. Они отвечают на вопрос, который на самом деле есть у списка комментариев или формы: готовы ли МОИ редакторы? Ограничьте область тем ref, который у вас уже есть на контейнере, — тогда посторонний редактор на странице не сможет держать проверку закрытой. Это живой сигнал, а не одноразовая защёлка: редактор, смонтированный позже, снова закрывает проверку, а с settleOn: 'rendered' — и каждый повторный рендер при смене data. Область без редакторов считается готовой, поэтому пустой список не требует отдельной ветки. Значение начинается с false и впервые читается по-настоящему, когда элемент области примонтирован (React — эффект после монтирования, Vue — onMounted, Angular — afterNextRender); запрошенная, но ещё не разрешённая область даёт false, а не молчаливый откат ко всей странице: лишнее ожидание безопасно, недостаточное является ошибкой.
Блочные инструменты
- ПараграфТекстовый блок по умолчанию. Поддерживает полноценное строчное форматирование (жирный, курсив, ссылки, цвет). Пустые параграфы верхнего уровня не попадают в сохраняемые данные, если не включён `preserveBlank`; пустые параграфы, вложенные в другой блок (выноска, переключатель, колонка, …), сохраняются всегда.
- ЗаголовокБлоки заголовков от H1 до H6. Поддерживают несколько пунктов тулбокса (по одному на уровень заголовка), сочетания клавиш (#, ##, ### и т. д.) и необязательный режим переключателя (сворачивание и разворачивание дочерних блоков) на любом уровне — в тулбоксе перечислены пункты от «Сворачиваемый заголовок 1» до «Сворачиваемый заголовок 6», доступные по markdown-префиксам `>#` … `>######`. Преобразование существующего блока в сворачиваемый заголовок (через «Преобразовать в» или `blocks.convert` с `isToggleable: true`) забирает его секцию: каждый следующий соседний блок до ближайшего заголовка того же или более высокого ранга становится дочерним для нового переключателя — как в Notion.
- СписокМаркированные, нумерованные списки и списки задач (чек-листы) с неограниченной вложенностью. Каждый пункт списка — отдельный блок. По умолчанию в тулбоксе показываются три пункта, по одному на каждый стиль, а пункты списка можно преобразовывать между стилями через меню настроек блока.
- ТаблицаПолнофункциональный блок таблицы. Каждая ячейка содержит собственный редактор блоков (любой тип блока, кроме `header`, `table` и `column_list` — они всегда запрещены внутри ячеек). Поддерживаются объединение и разделение ячеек (`colspan`/`rowspan`, перекрытые ячейки записываются как `mergedInto`), строки заголовков, столбцы заголовков, изменение ширины столбцов, цвет фона и текста ячеек, элементы управления для добавления и удаления строк и столбцов, копирование и вставка, а также переключение плотности текста (компактная или комфортная) в меню настроек блока.
- ПереключательСворачиваемый блок-переключатель с кликабельной стрелкой. Дочерние блоки вкладываются внутрь переключателя и скрываются, когда он свёрнут. Переключение выполняется кликом по иконке стрелки или программно через публичный Block API: `api.blocks.getById(id)?.call("expand")` / `.call("collapse")`. Сворачиваемые заголовки (блоки Header с `isToggleable: true`) принимают те же две команды. Это команды, адресуемые строкой и маршрутизируемые через `BlockAPI.call()`, — они не объявлены как методы экспортируемых классов инструментов. Состояние «открыт / свёрнут» сохраняется в `isOpen` и восстанавливается при перезагрузке; по умолчанию переключатели открыты.
- ВыноскаБлок-контейнер для выделенного содержимого с иконкой-эмодзи. Поддерживает настраиваемые цвета текста и фона через палитру цветов. Дочерние блоки вкладываются внутрь выноски. Удобен для советов, предупреждений, заметок и другого содержимого, привлекающего внимание. Enter добавляет строку внутри панели; повторное нажатие на пустой последней строке выводит из выноски, поэтому пустая строка становится параграфом ниже, а не растягивает панель.
- База данныхБлок базы данных с несколькими представлениями: доской (Kanban) и списком. Хранит схему типизированных свойств (text, select, multiSelect, date, checkbox и другие) и конфигурации представлений. Строки хранятся как дочерние блоки `database-row`. Поддерживаются группировка, изменение порядка перетаскиванием, редактирование на месте и необязательный адаптер синхронизации с бэкендом. (`sorts` и `filters` сохраняются в конфигурации представления, но пока не применяются.)
- Строка базы данныхВнутренний блочный инструмент, который хранит одну строку базы данных. Пользователь не может вставить его сам — строки создаются и управляются родительским блоком базы данных. Каждая строка хранит значения свойств, соответствующие схеме родительской базы данных, и строку позиции для упорядочивания.
- РазделительГоризонтальная линия-разделитель. Отрисовывает семантический элемент `<hr>`. Не имеет редактируемого содержимого и настроек. Вставляется через тулбокс или вводом `---` в пустом параграфе.
- ОтступРегулируемый вертикальный отступ. Чтобы изменить размер, потяните за любой из краевых захватов — или поставьте на него фокус и нажимайте ArrowUp/ArrowDown. Его главная задача — выравнивать содержимое соседних колонок разной длины, заменяя вереницы пустых параграфов. В режиме только для чтения невидим.
- ЦитатаЦитата с акцентной левой границей. Поддерживает два размера (обычный и крупный), которые переключаются через меню настроек блока. Вставка элемента `<blockquote>` автоматически создаёт блок цитаты.
- КодБлок кода с подсветкой синтаксиса, выбором языка, необязательной нумерацией строк и кнопкой копирования в буфер обмена. Поддерживает более 30 языков через Prism. У языков LaTeX и Mermaid есть вкладка с живым предпросмотром. Вставка markdown-блоков кода в тройных обратных кавычках (```) или элементов `<pre>` автоматически создаёт блок кода, а язык подхватывается всякий раз, когда вставляемый источник его называет, — блок, открывающийся строкой ```sql, или помеченный блок кода, скопированный из приложения вроде Gemini.
- ИзображениеИзображение, которое добавляется по URL, загрузкой файла или вставкой файла из буфера обмена.
- КолонкиБлок раскладки, который располагает дочерние блоки колонками бок о бок. Сам список колонок не хранит содержимого — каждая колонка представлена дочерним блоком `column`, а блоки, которые вы пишете, живут внутри этих колонок (через `contentIds`). Колонки создаются тремя способами: из тулбокса · перетаскиванием блока к другому блоку сбоку · выделением нескольких блоков и выбором «Преобразовать в колонки». Ширина колонок меняется разделителями между ними. Оба инструмента регистрируются разом групповой ссылкой `Columns` — `tools: { columns: Columns }` разворачивается в инструменты `column_list` и `column`; в сохранённом JSON по-прежнему лежат блоки `column_list` и `column`.
- КолонкаОдна колонка внутри списка колонок. Пользователь не может вставить её сам — колонки создаются и управляются родительским блоком `column_list`. Дочерние блоки вкладываются в колонку через `contentIds`. Необязательное поле `widthRatio` задаёт ширину колонки относительно соседних (применяется как flex-grow); опустите его, чтобы колонки были равной ширины.
- ВстраиваниеЖивой интерактивный iframe для вставленного URL провайдера (YouTube, Vimeo, Figma, CodePen и ещё более 100 сервисов) — как «Create embed» в Notion. Работает целиком на клиенте: URL сопоставляется со встроенным реестром встраиваний и превращается в разрешённый провайдером URL для iframe. По умолчанию встраиваются только URL, найденные в реестре; установите на уровне редактора `linkPaste.allowGenericEmbed: true`, чтобы встраивать и несовпавшие https-URL — в общий iframe в песочнице (сохраняется с пустым `service`). Поддерживаются изменение размера (у провайдеров документов — Google Docs, Sheets, Slides, Forms и Drive — есть ещё нижний захват для настройки высоты встраивания), выравнивание (влево, по центру, вправо) и необязательная подпись.
- ЗакладкаСтатичная карточка OpenGraph для вставленной ссылки — как «Create bookmark» в Notion. Показывает заголовок страницы, описание, изображение предпросмотра, favicon и домен. Метаданные запрашиваются с эндпоинта разворачивания ссылок, который предоставляет потребитель (из-за CORS бэкенд обязателен) — Blok поставляет только контракт.
- ФайлКарточка вложения для любого загруженного файла. Показывает иконку типа, имя файла, размер в понятном человеку виде, действие скачивания и необязательную подпись. Файлы отправляются через загрузчик, который предоставляет потребитель; если его нет, инструмент откатывается на локальный blob URL (uploadByFile) или на сам вставленный URL (uploadByUrl). Необязательный список разрешённых MIME-типов и максимальный размер ограничивают, что можно принять.
- АудиоБлок аудио в стиле музыкального плеера. Отрисовывает загруженный или указанный ссылкой аудиофайл с собственной панелью управления (воспроизведение и пауза, перемотка по форме волны, громкость, скорость воспроизведения, повтор), необязательной обложкой, метаданными названия и исполнителя и необязательной подписью, которая включается из меню настроек блока (`captionVisible`). Пики формы волны и длительность декодируются один раз и кэшируются в сохраняемых данных, поэтому при перезагрузке плеер отрисовывается мгновенно. Аудио отправляется через загрузчик, который предоставляет потребитель; если его нет, инструмент откатывается на локальный blob URL (uploadByFile) или на вставленный URL (uploadByUrl). Ссылки для общего доступа из Dropbox, GitHub, GitLab, Hugging Face, Google Cloud Storage и Internet Archive автоматически переписываются в прямые ссылки на содержимое; для ссылок Google Drive и OneDrive дополнительно нужен бэкенд `uploadByUrl`, потому что эти хостинги блокируют анонимные запросы из браузера. Необязательный список разрешённых MIME-типов и максимальный размер ограничивают, что можно принять.
- ВидеоПолнофункциональный блок видеоплеера. Отрисовывает загруженное или указанное ссылкой видео с собственной панелью управления (воспроизведение и пауза, полоса перемотки с буферизованным диапазоном и предпросмотром при наведении, громкость, скорость воспроизведения, повтор, «картинка в картинке», театральный и полноэкранный режимы), необязательной подписью и мягким свечением позади плеера. Видео отправляется через загрузчик, который предоставляет потребитель; если его нет, инструмент откатывается на локальный blob URL (uploadByFile) или на вставленный URL (uploadByUrl). Необязательный список разрешённых MIME-типов и максимальный размер ограничивают, что можно принять.
Строчные инструменты
- ЖирныйОборачивает выделенный текст в `<strong>`. Включается сочетанием Cmd/Ctrl+B или кнопкой B на строчной панели инструментов. Поддерживает вложенные жирные диапазоны и нормализует перекрывающуюся разметку при вставке.
- КурсивОборачивает выделенный текст в `<i>` (вставленный `<em>` тоже сохраняется). Включается сочетанием Cmd/Ctrl+I или кнопкой I на строчной панели инструментов.
- СсылкаОборачивает выделенный текст в `<a href="...">`. Включается сочетанием Cmd/Ctrl+K. Клик по кнопке на уже связанном тексте открывает поле ввода URL, позволяя изменить или удалить ссылку. `target` и `rel` всегда пишутся рядом с `href` и берутся из `BlokConfig.link` — по умолчанию `_blank` и `nofollow`, причём для ссылок на ту же страницу принудительно ставится `target="_self"` (это `#anchor` или URL, который разрешается в текущие origin и pathname). `link.transform` может переопределить любое из href, target и rel.
- МаркерПрименяет к выделенному тексту цвет текста или цвет фона через `<mark style="color:...">` или `<mark style="background-color:...">`. Нажмите кнопку на панели и выберите вкладку «Цвет текста» или «Фон». На каждой вкладке есть готовые образцы, предпросмотр выбранного цвета и сброс «По умолчанию». Недавно использованные цвета показаны под палитрой. Каждый цвет нормализуется в CSS-переменную (`var(--blok-color-<name>-<text|bg>)`), чтобы темы могли его переопределить: палитра предлагает только девять предустановленных цветов, а любой другой CSS-цвет, применённый программно, притягивается к перцептивно ближайшему из них. Порога расстояния нет — без изменений проходят только значения, уже записанные как `var(...)`, значения, которые парсер цвета прочитать не может (именованный CSS-цвет вроде `rebeccapurple`), и цвета фона страницы по умолчанию. Сырые цвета, найденные на элементах `<mark>` — например, вставленных из другого редактора, — при загрузке переписываются в ближайшую предустановленную переменную, поэтому произвольные hex-значения не сохраняются. Cmd/Ctrl+Shift+H палитру не открывает: это сочетание сразу применяет к выделению последний выбранный в этой сессии цвет, при первом использовании — жёлтую подсветку (`var(--blok-color-yellow-bg)`), и ничего не делает, когда выделение схлопнуто.
- ПодчёркнутыйОборачивает выделенный текст в `<u>`. Включается сочетанием Cmd/Ctrl+U или кнопкой U на строчной панели инструментов.
- ЗачёркнутыйОборачивает выделенный текст в `<s>`. Включается сочетанием Cmd/Ctrl+Shift+S или кнопкой S на строчной панели инструментов.
- Строчный кодОборачивает выделенный текст в `<code>`. Включается сочетанием Cmd/Ctrl+E или кнопкой кода на строчной панели инструментов. Удобно для имён переменных, вызовов функций и коротких фрагментов кода внутри текста.
- ФормулаОтрисовывает строчные формулы (LaTeX) с помощью KaTeX. Включается сочетанием Cmd/Ctrl+Shift+E — оборачивает выделенный текст или формулу, введённую в поле поповера, в `<span data-latex="...">`. Формула — это и есть атрибут `data-latex`: разметка KaTeX выводится из него, удаляется при сохранении и создаётся заново при каждой отрисовке блока (загрузка, вставка, отмена). Поверхности только для чтения, которые никогда не монтируют редактор, — `blocksToHtml` и `<BlokView>` — показывают исходный текст формулы; передайте запись `inlineRenderers`, чтобы формулы отрисовывались и там.
- Очистить форматСнимает с выделенного текста строчное форматирование (жирный, курсив, подчёркивание, зачёркивание, строчный код, подсветку), сохраняя ссылки. Применяется кликом по кнопке Tx на строчной панели инструментов.
- Надстрочный / подстрочныйОдна кнопка панели с поповером из двух вариантов, которая включает для выделения верхний (`<sup>`) или нижний (`<sub>`) индекс. Режимы взаимно исключают друг друга — применение одного снимает другой. Сочетания клавиш: Cmd/Ctrl+Period для верхнего индекса, Cmd/Ctrl+Comma для нижнего.