Skip to content
ФреймворкJavaScript

Параметры конфигурации Blok

Объект конфигурации, передаваемый в конструктор 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`, так что существующий код компилируется без изменений.

TypeScript
import { Blok, type BlokConfig } from '@bloklabs/core';

const config: BlokConfig = {
  holder: 'editor',
  placeholder: 'Start writing...',
  autofocus: true,
  readOnly: false,
  minHeight: 300,
};

const editor = new Blok(config);

Конфигурация

ПараметрТипПо умолчаниюОписание
holderstring | HTMLElement'blok'Контейнер (ID или элемент DOM)
toolsRecord<string, ToolConstructable | ToolSettings>{}Доступные блочные и строчные инструменты. По умолчанию не регистрируется ничего: значение `{}` оставляет только внутренние инструменты Blok (`stub`, `delete`, `copyLink`, `convertTo`), поэтому голый `new Blok({ holder })` не сможет отрисовать даже параграф. Из `@bloklabs/core/full` экспортируются два готовых набора — `defaultTools` (параграф, заголовок и список, все с `inlineToolbar: true`) и `allTools` (`defaultTools` плюс цитата, выноска, код, тоггл и все строчные инструменты). `toolbox: false` у отдельного инструмента оставляет его зарегистрированным (существующие блоки продолжают отображаться, blocks.insert() продолжает работать), но убирает его со всех путей вставки пользователем — из меню + / слэш, из меню конвертации и из сочетания клавиш. Удобно для разграничения прав — и переключается на лету через `tools.update(name, { toolbox })` (React-адаптер применяет изменения значений `toolbox` в пропе `tools` автоматически), так что смена прав никогда не требует пересоздания редактора.
tunesstring[]undefinedNames of block tunes added to every block tool that does not declare its own `tunes` set. The tune classes themselves must be registered in `tools` (a class with `static isTune = true`).
placeholderstring | falsefalseТекст-заполнитель, который получает каждый блок инструмента по умолчанию — не только первый блок и не только пока документ пуст. Со встроенным параграфом он виден всякий раз, когда блок пуст и находится в фокусе. Учтите, что `false` (значение по умолчанию) не убирает заполнитель: инструмент параграфа тогда использует собственный встроенный локализованный текст («Write something or press / to select a tool»). Чтобы очистить его, задайте инструменту по умолчанию собственный пустой placeholder — `tools: { paragraph: { class: Paragraph, placeholder: '' } }`.
minHeightnumber300Высота в px нижней кликабельной зоны редактора
captureClicksBelowEditorbooleanfalseОпция по требованию: клики по странице ниже редактора добавляют блок, не занимая места в раскладке. Сочетайте с `minHeight: 0`, чтобы полностью убрать нижнюю зону. Срабатывают только клики по пустому фону элемента, содержащего редактор — клики по вашему контенту, отрисованному ниже, игнорируются, а всплытие события не останавливается, так что обработчики кликов хост-страницы продолжают работать.
defaultBlockstring'paragraph'Тип блока по умолчанию
dataOutputData | LooseOutputData | nullundefinedНачальные данные для отображения. Принимается нестрогий формат: значения `null` для `data`, `id` или `time` блока (частые в backend-DTO) нормализуются на границе. Документ целиком, равный `null`, тоже допустим и превращается в пустой документ, поэтому nullable-состояние контролируемого компонента можно передавать как есть, без обёртки вида `value ?? { blocks: [] }`.
dataModel'legacy' | 'hierarchical' | 'auto''auto'Input/output data model. 'auto' detects the format of the data you render and preserves it on save; 'legacy' always uses the nested `items[]` structure; 'hierarchical' always uses flat blocks with `parent`/`content` references.
sanitizerSanitizerConfig{}Editor-wide default sanitizer allowlist. Composed with each tool's own `sanitize` rules and applied on save, on render, on paste and on copy of selected blocks.
readOnlyboolean | { hideControls?: boolean }falseВключить режим только для чтения. Передайте `{ hideControls: true }`, чтобы также скрыть hover-тулбар, настройки блока и строчную панель. Живое поле: меняется на лету через `readOnly.set(state, { hideControls })` — тот же экземпляр переключает режим на месте, сохраняя курсор, историю отмен и прокрутку.
onChange(api: API, event: BlockMutationEvent | BlockMutationEvent[]) => voidundefinedФункция обратного вызова при изменении; аргумент event несёт произошедшую мутацию (или массив мутаций, если несколько срабатывают одновременно) Задержка доставки ограничена, поэтому от него можно питать интерфейс: первое изменение простаивающего документа приходит уже на следующем микротаске — в том же кадре, в котором пользователь печатал, — а последующие изменения объединяются в один дополнительный вызов в конце короткого окна пакетирования, которое новые изменения не продлевают. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onChange })` — именно его наличие (вместе с `onSave`) включает конвейер отслеживания изменений.
onSave(data: OutputData, api: API) => voidundefinedРеактивный колбэк сохранения — срабатывает автоматически с полным сериализованным содержимым при каждом пакете изменений, поэтому save() вручную вызывать не нужно. Он срабатывает только по завершении окна пакетирования и, в отличие от onChange, никогда не опережает его: сериализовать весь документ слишком дорого. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onSave })` — само его наличие заставляет Blok сериализовать документ на каждую пачку изменений.
onReady(blok?: Blok) => voidundefinedСрабатывает один раз, когда редактор становится готов, получая полностью инициализированный экземпляр Blok
onEnter(event: KeyboardEvent, api: API) => boolean | voidundefinedСрабатывает при нажатии Enter в блоке, прежде чем Blok разделит его или создаст новый. Верните true, чтобы пометить событие обработанным — Blok отменит своё разделение/создание блока (нативный перенос строки всё равно предотвращается). Не срабатывает для инструментов с enableLineBreaks и пока Enter принадлежит поповеру или тулбару, а также для Shift+Enter с мягким переносом строки — кроме iOS, где Safari сообщает Shift+Enter для завершающего предложение «. », и Blok создаёт блок, поэтому хук срабатывает и там. Подходит для чат-полей («Enter отправляет») — сочетайте с настройкой preserveBlank параграфа вместо наследования от Paragraph. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onEnter })`.
onSubmit(data: OutputData, api: API) => voidundefinedСрабатывает с полными сериализованными OutputData на том Enter, который иначе создал бы или разделил блок, — жест «Enter отправляет». Blok сериализует документ и подавляет разделение по умолчанию, поэтому вызывать save() внутри onEnter вручную не нужно. Наследует все исключения onEnter; если заданы оба, onEnter, вернувший true, имеет приоритет и подавляет onSubmit. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onSubmit })` — передайте `undefined`, чтобы вернуть стандартное поведение Enter (разбиение блока) без пересоздания редактора.
onError(error: Error, context: { source: 'save' }) => voidundefinedСрабатывает, когда операция редактора завершается ошибкой, которую Blok иначе только записал бы в лог; сегодня единственный источник — сериализация. Через него проходят и отложенное автосохранение, и явный save(). Неудавшийся save() отклоняет промис с исходной ошибкой — он никогда не разрешается значением undefined, — поэтому оборачивайте явные сохранения в try/catch; onError дополнительно показывает сбои отложенного автосохранения, у которого собственного промиса нет.
onBeforePaste(html: string) => string | nullundefinedПреобразует исходный `text/html` из буфера обмена до какой-либо предобработки и санитизации в Blok, поэтому перехватчик вставки на фазе перехвата больше не нужен. Верните HTML, который попадёт в остальной конвейер вставки, или null, чтобы пропустить HTML-путь и вставить как обычный текст. Всё описанное ниже выполняется уже после вашего обработчика. Blok приводит в порядок разметку, которую другие приложения кладут в буфер обмена: ответ, скопированный из ChatGPT, Claude или Gemini, и страница, скопированная из Notion или Google Docs, становятся настоящими блоками (заголовки, списки, таблицы, цитаты, код), а не одним сплошным абзацем. Для ChatGPT и Gemini дополнительно выполняется отдельный предпроход, потому что каждый из них прячет смысл в разметке, которую санитайзер иначе удалил бы: ChatGPT не отдаёт MathML, поэтому LaTeX формулы восстанавливается из атрибута с исходником и снова собирается в формулу, а его блоки кода дедуплицируются (каждый отрисован как вложенный редактор); у Gemini язык блока кода считывается с подписи над блоком. Для Claude отдельный предпроход не нужен — его ответы и так приходят семантическим HTML и проходят по обычным путям HTML и markdown.
onBeforeRender(blocks: OutputBlockData[]) => OutputBlockData[]undefinedПреобразует массив блоков прямо перед отображением — при первом рендере, при каждом вызове `blocks.render()` и при перерисовках, которые Blok выполняет сам (рантайм-вызов `i18n.update()` и повторный рендер как запасной путь переключения режима только для чтения). Получает сохранённые блоки в исходном виде (до анализа формата и раскрытия иерархии) и возвращает блоки для отображения, поэтому миграции данных приложения выполняются внутри Blok, а не заранее. Из-за этого функция обязана быть идемпотентной: перечисленные перерисовки подают ей блоки, которые она уже преобразовала. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onBeforeRender })`.
onAfterRender(api: API) => voidundefinedСрабатывает после того, как очередная партия рендера попадает в DOM: при первом рендере, при каждом `blocks.render()` и при перерисовках, которые Blok выполняет сам, — смене локали/сообщений через рантайм-вызов `i18n.update()` и переключении `readOnly.set()`, которое откатывается к полному повторному рендеру, потому что смонтированный инструмент не поддерживает смену режима только для чтения на месте. Используйте для побочных эффектов после рендера (восстановление прокрутки, подключение наблюдателей), помня об этих дополнительных срабатываниях, если считаете рендеры. Отличается от onReady, который срабатывает один раз, когда редактор впервые становится готов. Живое: устанавливается, заменяется или снимается в рантайме через `handlers.set({ onAfterRender })`.
autofocusbooleanfalseЕсли true, устанавливает курсор в первый блок, как только редактор готов
scrollToBlock{ topOffset?: number }undefinedBlok always smooth-scrolls to the block whose id matches the page URL hash (`#<blockId>`) once blocks are rendered — including blocks rendered later via `blocks.render()`. This option only tunes that behavior: `topOffset` (default 0) reserves space above the block for a sticky header.
inlineToolbarstring[] | booleantrueСтрочная панель по умолчанию для всех инструментов; массив ограничивает её перечисленными строчными инструментами, false отключает её. Живое поле: перенастраивается на лету через `tools.setInlineToolbar(config)`.
hideToolbarbooleanfalseСкрыть hover-тулбар блока (кнопку «плюс» / ручку перетаскивания) и убрать зарезервированный под него отступ редактора; меню по клавише "/" продолжает работать. Живое поле: переключается на лету через `toolbar.setHidden(hidden)`.
toolbarPosition'left' | 'right''left'С какой стороны от колонки контента находятся плавающие элементы управления блоком (кнопка «плюс» и ручка перетаскивания/меню). `'right'` переносит и элементы управления, и зарезервированный под них отступ на inline-end сторону редактора: начальный отступ схлопывается, а в конце открывается такой же — текст занимает место, которое раньше занимали элементы управления. Значения называют физическую сторону для LTR и применяются через логические свойства, поэтому в RTL-редакторе они зеркалятся. Не действует, пока включён `hideToolbar` или read-only без элементов управления — размещать нечего. Живое поле: переключается на лету через `toolbar.setPosition(position)`.
i18nI18nConfigundefinedКонфигурация интернационализации (локаль + словарь сообщений). Живое поле: меняйте язык на лету через `i18n.update({ locale, messages })` — редактор меняет подписи на месте, поэтому курсор и история отмен переживают смену языка (`defaultLocale` — исключение и читается только при монтировании). Заголовки пользовательских инструментов локализуются по имени регистрации — например, инструмент `fileLink` через `messages: { 'toolNames.fileLink': '…' }` — либо через `titleKey` в записи toolbox инструмента.
uploaderBlokUploaderundefinedEditor-level uploader for every media asset, routed by asset KIND rather than by tool. `uploadByFile(file, { kind, tool })` and `uploadByUrl(url, { kind, tool })` receive `kind: 'image' | 'video' | 'audio' | 'file'`, so one implementation serves the image, video, audio and file blocks — including assets a tool owns outside its own media family, such as the audio block's cover art (`kind: 'image'`, `tool: 'audio'`), which has no tool-level uploader of its own. A tool-level uploader (`tools.image.config.uploader`) stays authoritative for its own kind and takes precedence; this is the fallback. Without either, assets become `blob:` URLs that do not survive a reload.
theme'auto' | 'light' | 'dark''auto'Цветовая тема; 'auto' следует настройке ОС через prefers-color-scheme
onThemeChange(resolvedTheme: ResolvedTheme) => voidundefinedFires with the RESOLVED theme ('light' or 'dark') whenever it changes — both when the OS preference flips while `theme` is 'auto', and when `theme.set()` changes what the theme resolves to. It does not fire on initialization, and it does not fire when a `theme.set()` leaves the resolved theme unchanged. A core config option, not an adapter-only prop; the framework adapters expose the same callback as the `onThemeChange` prop / `theme-change` emit / `themeChange` output.
linkPaste{ allowGenericEmbed?: boolean; allowedEmbedOrigins?: string[] }undefinedNotion-style link-paste behavior. Set `allowGenericEmbed: true` to also offer "Create embed" (framed in a sandboxed iframe) for URLs that match no registered embed provider; the default keeps Blok's registry-only embed guarantee. `allowedEmbedOrigins` is the fine-grained middle ground: hostnames (`dashboards.example.com`) or wildcard subdomain patterns (`*.internal.example.dev`) that may be framed as generic embeds. A stored generic embed matching neither renders as a safe clickable link card instead of an iframe, so the URL stays visible without being framed.
user{ id: string }undefinedIdentity of the current editor. Blok stamps `user.id` onto the `lastEditedBy` of every block this user edits — without it `lastEditedBy` stays null. Pair it with `resolveUser` to render a name in the block settings footer.
resolveUser(id: string) => UserInfo | Promise<UserInfo | null> | nullundefinedОпределяет пользователя по идентификатору `lastEditedBy`, который Blok показывает в подвале настроек блока. Может возвращать значение синхронно или асинхронно; верните null для неизвестного пользователя — тогда Blok покажет только дату.
notifierPositionNotifierPosition'bottom-center'Where the built-in toast container is anchored on screen.
notifier(options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions) => voidundefinedReplaces the built-in toast entirely — Blok calls your handler with the same options object instead of rendering its own DOM notification.
logLevelLogLevelsLogLevels.VERBOSEHow much Blok logs to the console. Values are `VERBOSE`, `INFO`, `WARN` and `ERROR` — there is no "silent" level, so `LogLevels.ERROR` is the quietest. `LogLevels` is a named export of the package root.