ФреймворкJavaScript
Blok documentation
Guides, the full API reference, and every built-in block and inline tool. New here? Start with the quick start and have an editor running in five minutes.
Начало работы
- Быстрый стартНачните работу с 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` в конфигурации конструктора — hover-тулбар тогда не открывается, обёртка получает `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 по вертикали у цитат, 5px по вертикали у выносок, — поэтому одно переопределение перенастраивает все блоки сразу; именно это нужно хосту в режиме только для чтения для плотной строчной вёрстки (раньше это было возможно только переопределением внутренностей `[data-blok-tool]`). Учтите, что нестандартные отступы слегка сдвигают производную геометрию — например, смещение стрелки заголовка-тоггла, которое следует за `--blok-block-padding-top`. Вложенность блоков публична так же: блок, вложенный в другой (Tab на верхнем уровне), получает отступ `--blok-block-indent-step` на каждый уровень (по умолчанию `24px`), и это настоящий CSS, а не инлайновый стиль, поэтому обычное правило хоста меняет или убирает его без `!important`. Blok обнуляет шаг внутри каждого дочернего слота `[data-blok-nested-blocks]` — этот маркер рендерит для своих детей любой контейнерный инструмент, встроенный и сторонний, — поэтому блоки, которые контейнер уже расположил сам, не сдвигаются дополнительно по своей глубине; контейнеру, которому отступ нужен, достаточно объявить шаг обратно на своём слоте. Размер текста публичен и для блока, И для сценария через `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()` — но каналы не взаимозаменяемы, и `style.fontSize` сильнейший из трёх: настройка сценария внедряет таблицу стилей, которая при равной специфичности оказывается в `<head>` позже таблицы токенов темы, поэтому последующий `editor.tokens.set({ '--blok-paragraph-font-size': … })` (или CSS-правило на предке) для того же сценария молча ей проигрывает. Поэтому, если размер должен меняться после монтирования — переключатель плотности, масштаба или доступности, — управляйте им через `style.tokens` / `editor.tokens.set()` и вовсе не указывайте этот сценарий в `style.fontSize`; сам `style.fontSize` читается один раз при создании и не реактивен. Как и `style.tokens`, внедряемая таблица действует на всю страницу, а не на отдельный экземпляр: она меняет типографику каждого редактора Blok на странице, включая экземпляры, созданные без собственного `style.fontSize`, поэтому различия между экземплярами задавайте CSS-правилом на собственной обёртке каждого редактора. Стоит знать одно правило вложенности: выноска отображает свой текст как дочерний блок-параграф, поэтому текст выноски следует `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' снимает ограничение, и содержимое заполняет свой контейнер.
- ПлейсхолдерЧтение и смена плейсхолдера уровня редактора (подсказки в пустом блоке инструмента по умолчанию) на лету, без пересоздания редактора.
Расширение и система
- ИнструментыДоступ и управление инструментами редактора.
- ЗагрузчикUpload an asset through the pipeline that owns its KIND, instead of whichever tool happens to be asking. Tools call this rather than reaching into their own `config.uploader`, which is why an audio block's cover art reaches your image pipeline instead of the audio endpoint that would reject it. Resolution order for a kind: the tool whose static `assetKind` matches (e.g. `tools.image.config.uploader` for `'image'`), then the editor-level `uploader` config, then a local fallback — a `blob:` URL for files, the URL verbatim for links. See the storage presets page for ready-made `uploader` implementations — Supabase, S3-compatible storage, Cloudinary, and IndexedDB — that need no backend of your own.
- СобытияПодписка и управление событиями жизненного цикла редактора.
- СлушателиУправление пользовательскими обработчиками 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, inlineToolbar, theme, width, placeholder, styleTokens, i18n, autofocus, migrations, onBeforeRender, onBeforePaste, onError — плюс запасной вход `[config]` для всех остальных ключей конфигурации (sanitizer, minHeight, defaultBlock, dataModel, link, linkPaste, tunes, user, resolveUser, uploader, notifier, logLevel, onEnter, onSubmit, scrollToBlock, …) и не пробрасывает атрибуты хоста на контейнерный div. Живой экземпляр Blok читается через ref/onReady (React), через `instance` на шаблонной ссылке или эмит `@ready` (Vue) и через сигнал `instance` или output `(ready)` (Angular). Пропы ниже описывают специфичную для адаптеров поверхность; остальное совпадает с опциями раздела «Конфигурация».
- useBlocksРеактивный снимок дерева блоков плюс полный API манипуляций от адаптеров фреймворков: хук useBlocks(editor) в @bloklabs/react, composable useBlocks(editor) в @bloklabs/vue и `injectBlocks(editor)` в @bloklabs/angular — передайте сигнал `instance` у BlokEditorComponent/BlokContentDirective и вызывайте из контекста внедрения (инициализатора поля или конструктора). Чтения реактивно перерисовываются при изменении документа; записи атомарны (один шаг отмены) и безопасны до готовности редактора (превращаются в no-op). Возвращаемые объекты BlockNode ({ id, type, parentId, contentIds }) — волатильные свежие снимки: читайте их сразу, не сохраняйте в массивах зависимостей.
- 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, а не молчаливый откат ко всей странице: лишнее ожидание безопасно, недостаточное является ошибкой.
Блочные инструменты
- ПараграфThe default text block. Supports rich inline formatting (bold, italic, links, colour). Empty top-level paragraphs are excluded from saved output unless `preserveBlank` is enabled; empty paragraphs nested inside another block (callout, toggle, column, …) are always kept.
- ЗаголовокHeading blocks from H1 to H6. Supports multiple toolbox entries (one per heading level), keyboard shortcuts (# ## ### etc.), and optional toggle (collapse/expand children) at every level — the toolbox lists "Toggle heading 1" through "Toggle heading 6", reachable with the markdown shortcuts `>#` through `>######`. Converting an existing block into a toggle heading (via "Turn into" or `blocks.convert` with `isToggleable: true`) adopts its section — every following sibling until the next heading of the same or higher rank becomes a child of the new toggle, matching Notion.
- СписокBulleted, numbered, and to-do (checklist) lists with unlimited nesting. Each list item is a separate block. The toolbox shows three entries by default — one for each style — and items can be converted between styles via the block settings menu.
- ТаблицаA full-featured table block. Each cell contains its own block editor (any block type except `header`, `table` and `column_list`, which are always restricted inside cells). Supports merging and splitting cells (`colspan`/`rowspan`, with covered cells recorded as `mergedInto`), heading rows, heading columns, column resizing, cell background/text colours, row/column add and delete controls, copy/paste, and a text density switch (compact or comfortable) in the block settings menu.
- ПереключательA collapsible toggle block with a clickable arrow. Child blocks are nested inside the toggle and hidden when collapsed. Toggling is controlled by clicking the arrow icon, or programmatically via the public Block API: `api.blocks.getById(id)?.call("expand")` / `.call("collapse")`. Toggle headings (Header blocks with `isToggleable: true`) accept the same two commands. These are string-addressed commands routed through `BlockAPI.call()` — they are not declared as methods on the exported tool classes. The open/collapsed state is persisted via `isOpen` and restored on reload; toggles default to open.
- ВыноскаA container block for highlighted content with an emoji icon. Supports customisable text and background colours via a colour picker. Child blocks are nested inside the callout. Useful for tips, warnings, notes, and other call-to-action content. Enter adds a line inside the panel; pressing it again on the empty last line leaves the callout, so the blank line becomes the paragraph below instead of padding the panel out.
- База данныхA multi-view database block supporting board (Kanban) and list views. Stores a schema of typed properties (text, select, multiSelect, date, checkbox, etc.) and view configurations. Rows are stored as child `database-row` blocks. Supports grouping, drag-and-drop reordering, inline editing, and an optional backend sync adapter. (`sorts` and `filters` are persisted in the view config but are not applied yet.)
- Строка базы данныхAn internal block tool that stores a single database row. Not user-insertable — rows are created and managed by the parent Database block. Each row stores property values conforming to the parent database schema and a position string for ordering.
- РазделительA horizontal line separator. Renders a semantic `<hr>` element. Has no editable content or settings. Can be inserted via the toolbox or by typing `---` in an empty paragraph.
- ОтступAn adjustable vertical gap. Drag either edge grip — or focus one and press ArrowUp/ArrowDown — to resize. Its main job is lining up content across sibling columns of unequal length, replacing piles of empty paragraphs. Invisible in read-only mode.
- ЦитатаA blockquote with a left border accent. Supports two sizes (default and large) switchable via the block settings menu. Pasting a `<blockquote>` element automatically creates a quote block.
- КодA syntax-highlighted code block with a language picker, an optional line-number gutter, and a copy-to-clipboard button. Supports 30+ languages via Prism. LaTeX and Mermaid languages include a live preview tab. Pasting markdown fenced code blocks (```) or `<pre>` elements automatically creates a code block, and the language comes across whenever the pasted source names it — a fence that opens with ```sql, or a labelled code block copied out of an app such as Gemini.
- ИзображениеEmbed an image via URL upload or file paste.
- КолонкиA layout block that arranges its children into side-by-side columns. The column list itself holds no content — each column is a child `column` block, and the blocks you write live inside those columns (via `contentIds`). Columns can be created three ways: from the toolbox · by dragging a block beside another · by selecting multiple blocks and choosing "Turn into columns". Column widths are resizable via the separators between columns. Both tools can be registered at once with the `Columns` group handle — `tools: { columns: Columns }` expands to the `column_list` and `column` tools; saved JSON still contains `column_list` and `column` blocks.
- КолонкаA single column inside a column list. Not user-insertable on its own — columns are created and managed by the parent `column_list` block. Child blocks are nested inside the column via `contentIds`. The optional `widthRatio` controls the column’s width relative to its siblings (applied as flex-grow); omit it for equal width.
- ВстраиваниеA live interactive iframe for a pasted provider URL (YouTube, Vimeo, Figma, CodePen, and 100+ other services), like Notion’s "Create embed". Pure client-side: the URL is matched against a built-in embed registry and resolved into a provider-sanctioned iframe URL. By default only registry-matched URLs are embedded; set the editor-level `linkPaste.allowGenericEmbed: true` to also embed unmatched https URLs in a generic sandboxed iframe (saved with an empty `service`). Supports resizing (document-style providers such as Google Docs, Sheets, Slides, Forms and Drive also get a bottom handle for adjusting the embed height), alignment (left/center/right), and an optional caption.
- ЗакладкаA static OpenGraph card for a pasted link, like Notion’s "Create bookmark". Shows the page title, description, preview image, favicon, and domain. Metadata is fetched from a consumer-supplied unfurl endpoint (CORS makes a backend mandatory) — Blok ships only the contract.
- ФайлAn attachment card for any uploaded file. Shows a type icon, filename, human-readable size, a download action, and an optional caption. Files are sent through a consumer-supplied uploader; when none is provided the tool falls back to a local blob URL (uploadByFile) or the pasted URL itself (uploadByUrl). An optional MIME allowlist and max size can gate what is accepted.
- АудиоA music-player style audio block. Renders an uploaded or linked audio file with a custom control bar (play/pause, a waveform scrubber, volume, playback speed, loop), optional cover art, title/artist metadata, and an optional caption you switch on from the block settings menu (`captionVisible`). Waveform peaks and duration are decoded once and cached in the saved data so playback renders instantly on reload. Audio is sent through a consumer-supplied uploader; when none is provided the tool falls back to a local blob URL (uploadByFile) or the pasted URL (uploadByUrl). Share links from Dropbox, GitHub, GitLab, Hugging Face, Google Cloud Storage, and the Internet Archive are rewritten to their direct-content form automatically; Google Drive and OneDrive links additionally require an `uploadByUrl` backend because those hosts block anonymous browser hotlinking. An optional MIME allowlist and max size gate what is accepted.
- ВидеоA full-featured video player block. Renders an uploaded or linked video with a custom control bar (play/pause, scrubber with buffered range and hover preview, volume, playback speed, loop, picture-in-picture, theater and fullscreen modes), an optional caption, and an ambient glow behind the player. Videos are sent through a consumer-supplied uploader; when none is provided the tool falls back to a local blob URL (uploadByFile) or the pasted URL (uploadByUrl). An optional MIME allowlist and max size gate what is accepted.
Строчные инструменты
- ЖирныйWraps selected text in `<strong>`. Activated with Cmd/Ctrl+B or by clicking the B button in the inline toolbar. Supports nested bold ranges and normalises overlapping markup on paste.
- КурсивWraps selected text in `<i>` (pasted `<em>` is also preserved). Activated with Cmd/Ctrl+I or by clicking the I button in the inline toolbar.
- СсылкаWraps selected text in `<a href="...">`. Activated with Cmd/Ctrl+K. Clicking the button on existing linked text opens the URL input allowing the link to be edited or removed. `target` and `rel` are always written alongside `href` and come from `BlokConfig.link` — defaults `_blank` and `nofollow`, with `target="_self"` forced for same-page hrefs (a `#anchor`, or a URL resolving to the current origin and pathname). A `link.transform` can override any of href, target and rel.
- МаркерApplies text colour or background colour to selected text using `<mark style="color:...">` or `<mark style="background-color:...">`. Clicking the toolbar button opens a colour picker with preset text and background swatches plus a Default reset. Every colour is normalised to a CSS custom property (`var(--blok-color-<name>-<text|bg>)`) so themes can restyle it: the picker offers only the nine presets, and any other CSS colour applied programmatically is snapped to the perceptually nearest preset. There is no distance threshold — only values already written as `var(...)`, values the colour parser cannot read (a CSS named colour such as `rebeccapurple`), and the default page background colours pass through untouched. Raw colours found on `<mark>` elements — e.g. pasted from another editor — are rewritten to the nearest preset var on load, so arbitrary hex values are not preserved. Cmd/Ctrl+Shift+H does not open the picker: it re-applies the last colour picked in this session straight to the selection, defaulting to a yellow highlight (`var(--blok-color-yellow-bg)`) on first use, and does nothing when the selection is collapsed.
- ПодчёркнутыйWraps selected text in `<u>`. Activated with Cmd/Ctrl+U or by clicking the U button in the inline toolbar.
- ЗачёркнутыйWraps selected text in `<s>`. Activated with Cmd/Ctrl+Shift+S or by clicking the S button in the inline toolbar.
- Строчный кодWraps selected text in `<code>`. Activated with Cmd/Ctrl+E or by clicking the code button in the inline toolbar. Useful for marking up variable names, function calls, and short code snippets within text.
- ФормулаRenders inline math (LaTeX) with KaTeX. Activated with Cmd/Ctrl+Shift+E — wraps the selected text, or a formula typed into the popover input, in a `<span data-latex="...">`. The `data-latex` attribute is the formula: the KaTeX markup is derived from it, is stripped on save, and is regenerated whenever the block renders (load, paste, undo). Read-only surfaces that never mount an editor — `blocksToHtml` / `<BlokView>` — display the source instead; pass an `inlineRenderers` entry to render the math there too.
- Очистить форматRemoves inline formatting (bold, italic, underline, strikethrough, inline code, highlight) from the selected text while keeping links intact. Applied by clicking the Tx button in the inline toolbar.
- Superscript & SubscriptOne toolbar button with a two-option popover that toggles superscript (`<sup>`) or subscript (`<sub>`) on the selection. The two modes are mutually exclusive — applying one removes the other. Shortcuts: Cmd/Ctrl+Period for superscript, Cmd/Ctrl+Comma for subscript.