Параметры конфигурации 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, поэтому существующий код компилируется без изменений.
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);Конфигурация
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
holder | string | HTMLElement | 'blok' | Контейнер (ID или элемент DOM) |
tools | Record<string, ToolConstructable | ToolSettings> | {} | Доступные блочные и строчные инструменты. По умолчанию не регистрируется ничего. Значение Из
Значение переключается на лету через |
tunes | string[] | undefined | Имена блочных тюнов, которые добавляются каждому блочному инструменту, не объявившему собственный набор |
placeholder | string | false | false | Плейсхолдер, который получает каждый блок инструмента по умолчанию. Он достаётся каждому блоку, не только первому, и не только пока документ пуст. Со встроенным параграфом он виден всякий раз, когда блок пуст и находится в фокусе. Учтите: Чтобы очистить его, задайте инструменту по умолчанию собственный пустой плейсхолдер: |
minHeight | number | 300 | Высота в px нижней кликабельной зоны редактора |
captureClicksBelowEditor | boolean | false | Опция по требованию: клики по странице ниже редактора добавляют блок и не занимают места в раскладке. Сочетайте её с Срабатывают только клики по пустому фону элемента, который содержит редактор. Клики по вашему контенту, отрисованному ниже, игнорируются. Всплытие события не останавливается, поэтому обработчики кликов хост-страницы продолжают работать. |
inlineEmoji | boolean | true | Меню инлайн-эмодзи. Открывается при вводе Двоеточие открывает меню только в начале слова. То есть в начале блока или сразу после пробела. Ввод закрывающего Esc закрывает меню. Введённый текст при этом не меняется. Пока меню открыто, страница не прокручивается. Собственная сетка меню при этом прокручивается. У меню нет поля поиска, кнопки случайного выбора и кнопки удаления. Раздела с подборкой тоже нет. Всё это остаётся в собственном пикере эмодзи инструмента «Выноска», на который эта опция не влияет.
|
find | boolean | FindConfig | true | Поиск по странице. Cmd/Ctrl+F внутри редактора открывает собственную панель поиска Blok вместо браузерной. Панель открывается в правом верхнем углу окна, там же, где браузеры показывают свою панель поиска, и остаётся поверх всего на странице. Если выделен короткий фрагмент текста в одну строку, в редакторе или в полях вашей страницы, например в заголовке, он попадает в поле поиска, и поиск начинается с него. Передайте объект, чтобы выбрать место панели, например
По умолчанию сравнение нестрогое.
Совпадение может проходить через жирный текст, курсив и ссылки. Оно никогда не захватывает два блока или две ячейки таблицы. Поиск охватывает текст на всей странице и во всех редакторах Blok, включая контент только для чтения, блоки кода, ячейки таблиц и дочерние блоки свёрнутых сворачиваемых списков. Enter и Shift+Enter переходят к следующему и предыдущему совпадению, как и стрелки вниз и вверх в поле поиска. Cmd/Ctrl+G и F3 делают то же самое, а с Shift идут назад. Счётчик показывает текущую позицию, например Ввод запроса никогда не разворачивает свёрнутый список, но совпадения внутри него учитываются. Переход к такому совпадению разворачивает его список. Это работает для клавиш, кнопок-стрелок и карты совпадений. В редактируемом редакторе это обычное изменение документа, как щелчок по стрелке списка. Оно сохраняется и отменяется. Esc закрывает панель и выделяет текущее совпадение, только если это редактируемый текст в этом редакторе. Ввод затем заменяет его. Повторное нажатие Cmd/Ctrl+F, пока фокус в панели, выделяет текст поиска. Поиск браузера при этом не открывается. Замена открывается кнопкой-шевроном, сочетанием Cmd+Option+F на macOS или Ctrl+H на других системах. В поле замены Enter заменяет текущее совпадение, если оно находится в этом редакторе, и переходит к следующему. Cmd/Ctrl+Enter заменяет все редактируемые совпадения в этом редакторе одним шагом отмены. Текст в других частях страницы никогда не заменяется. В режиме только для чтения замена недоступна. Пока вы вводите замену, в редактируемом тексте этого редактора виден результат ещё до замены. Каждое совпадение зачёркнуто, сразу за ним стоит замена, а строки переносятся так, как будут перенесены. Предпросмотр не сохраняется, не попадает в историю отмены, и на него нельзя нажать. Совпадения подсвечиваются через CSS Custom Highlight API. В ваш контент не добавляется никакая разметка, поэтому поиск никогда не меняет документ. В браузере без этого API у совпадений нет заливки. Кольцо вокруг текущего совпадения всё равно видно, и переход между совпадениями работает. Цвета заданы токенами темы, они перечислены в разделе «API стилей». Cmd/Ctrl+F в полях вашей страницы, например в заголовке страницы или в поле поиска, тоже открывает поиск Blok, как и Cmd/Ctrl+F, когда ничего не в фокусе. Поле, которое само обрабатывает Cmd/Ctrl+F и вызывает |
defaultBlock | string | 'paragraph' | Тип блока по умолчанию |
data | OutputData | LooseOutputData | null | undefined | Начальные данные для отображения. Принимается нестрогий формат: значения Поэтому nullable-состояние контролируемого компонента можно передавать как есть, без обёртки вида |
dataModel | 'legacy' | 'hierarchical' | 'auto' | 'auto' | Модель входных и выходных данных. 'auto' определяет формат данных, которые вы отображаете, и сохраняет их в том же формате. 'legacy' всегда использует вложенную структуру |
sanitizer | SanitizerConfig | {} | Общий для редактора allowlist санитайзера по умолчанию. Blok объединяет его с собственными правилами |
readOnly | boolean | { hideControls?: boolean } | false | Включить режим только для чтения. Передайте |
onChange | (api: API, event: BlockMutationEvent | BlockMutationEvent[]) => void | undefined | Функция обратного вызова при изменении. Аргумент event несёт произошедшую мутацию или массив мутаций, если несколько срабатывают одновременно. Задержка доставки ограничена, поэтому от неё можно питать интерфейс. Первое изменение простаивающего документа приходит уже на следующем микротаске, в том же кадре, в котором пользователь печатал. Последующие изменения объединяются в один дополнительный вызов в конце короткого окна пакетирования, и новые изменения это окно не продлевают.
Живое поле: обработчик ставится, заменяется или снимается на лету через |
onSave | (data: OutputData, api: API) => void | undefined | Реактивный колбэк сохранения. Срабатывает автоматически с полным сериализованным содержимым на каждый пакет изменений, поэтому вызывать save() вручную не нужно. Он срабатывает только по завершении окна пакетирования. В отличие от onChange он никогда не опережает окно: сериализовать весь документ слишком дорого, чтобы делать это заранее. Живое поле: обработчик ставится, заменяется или снимается на лету через При синхронизации вкладок его вызывает только вкладка, в которой вы работаете. В фоновых вкладках читайте документ через |
onReady | (blok?: Blok) => void | undefined | Срабатывает один раз, когда редактор становится готов, получая полностью инициализированный экземпляр Blok |
onEnter | (event: KeyboardEvent, api: API) => boolean | void | undefined | Срабатывает при нажатии Enter в блоке, прежде чем Blok разделит его или создаст новый. Верните true, чтобы пометить событие обработанным: тогда Blok отменит своё разделение или создание блока (нативный перенос строки всё равно предотвращается). Не срабатывает для инструментов с enableLineBreaks, пока Enter принадлежит поповеру или панели инструментов, а также для Shift+Enter с мягким переносом строки. Исключение только на iOS: Safari сообщает Shift+Enter для завершающего предложение «. », и Blok создаёт блок, поэтому хук срабатывает и там. Подходит для чат-полей («Enter отправляет»). Сочетайте его с настройкой preserveBlank параграфа вместо наследования от Paragraph. Живое поле: обработчик ставится, заменяется или снимается на лету через |
onSubmit | (data: OutputData, api: API) => void | undefined | Срабатывает с полными сериализованными OutputData на том Enter, который иначе создал бы или разделил блок. Это жест «Enter отправляет». Blok сериализует документ и подавляет разделение по умолчанию, поэтому вызывать save() внутри onEnter вручную не нужно. Колбэк наследует все исключения onEnter. Если заданы оба, то onEnter, вернувший true, имеет приоритет и подавляет onSubmit. Живое поле: обработчик ставится, заменяется или снимается на лету через |
onError | (error: Error, context: { source: 'save' }) => void | undefined | Срабатывает, когда операция редактора завершается ошибкой, которую Blok иначе только записал бы в лог. Сегодня такие ошибки даёт только сериализация. Через колбэк проходят и отложенное автосохранение, и явный save(). Неудавшийся save() отклоняет промис с исходной ошибкой и никогда не разрешается значением undefined, поэтому оборачивайте явные сохранения в try/catch. Кроме того, onError показывает сбои отложенного автосохранения, у которого собственного промиса нет. |
onBeforePaste | (html: string) => string | null | undefined | Преобразует исходный
Всё описанное ниже выполняется уже после вашего обработчика. Blok приводит в порядок разметку, которую другие приложения кладут в буфер обмена. Скопируйте ответ из ChatGPT, Claude или Gemini или страницу из Notion или Google Docs. Содержимое станет настоящими блоками, а не одним сплошным абзацем. Это заголовки, списки, таблицы, цитаты и код. Для ChatGPT и Gemini дополнительно выполняется отдельный предпроход, потому что каждый из них прячет смысл в разметке, которую санитайзер иначе удалил бы.
Для Claude отдельный предпроход не нужен: его ответы и так приходят семантическим HTML и проходят по обычным путям HTML и markdown. |
onBeforeRender | (blocks: OutputBlockData[]) => OutputBlockData[] | undefined | Преобразует массив блоков прямо перед отображением. Работает при первом рендере, при каждом вызове Получает сохранённые блоки в исходном виде, до анализа формата и раскрытия иерархии, и возвращает блоки для отображения. Так миграции данных приложения выполняются внутри Blok, а не заранее. Из-за этого функция обязана быть идемпотентной: перечисленные перерисовки подают ей блоки, которые она уже преобразовала. Живое поле: обработчик ставится, заменяется или снимается на лету через |
onAfterRender | (api: API) => void | undefined | Срабатывает после того, как очередная партия рендера попадает в DOM. Это первый рендер, каждый вызов К последним относится смена локали или сообщений через рантайм-вызов Используйте колбэк для побочных эффектов после рендера: восстановления прокрутки, подключения наблюдателей. Помните об этих дополнительных срабатываниях, если считаете рендеры. Он отличается от onReady, который срабатывает один раз, когда редактор впервые становится готов. Живое поле: обработчик ставится, заменяется или снимается на лету через |
autofocus | boolean | false | Если true, устанавливает курсор в первый блок, как только редактор готов |
scrollToBlock | { topOffset?: number } | undefined | Blok всегда плавно прокручивает к блоку, чей id совпадает с хешем URL страницы ( |
inlineToolbar | string[] | boolean | true | Строчная панель инструментов по умолчанию для всех инструментов. Массив ограничивает её перечисленными строчными инструментами, а false отключает её. Массив выбирает, какие инструменты показать, но не их порядок. Панель всегда рисует встроенные инструменты в одном фиксированном порядке (преобразование, цвет, полужирный, курсив, подчёркнутый, очистка форматирования, ссылка, зачёркнутый, строчный код, формула, над/подстрочный). Любой пользовательский строчный инструмент добавляется после них, в порядке регистрации. Живое поле: перенастраивается на лету через |
hideToolbar | boolean | false | Скрывает hover-панель инструментов блока (кнопку «плюс» и ручку перетаскивания) и убирает зарезервированный под неё отступ редактора. Меню по клавише "/" продолжает работать. Живое поле: переключается на лету через |
toolbarPosition | 'left' | 'right' | 'left' | С какой стороны от колонки контента находятся плавающие элементы управления блоком (кнопка «плюс» и ручка перетаскивания или меню). Значения называют физическую сторону для LTR и применяются через логические свойства, поэтому в RTL-редакторе они зеркалятся. Не действует, пока включён Живое поле: сторона меняется на лету через |
i18n | I18nConfig | undefined | Конфигурация интернационализации (локаль и словарь сообщений). Живое поле: меняйте язык на лету через Редактор меняет подписи на месте, поэтому курсор и история отмен переживают смену языка. Исключение это Заголовки пользовательских инструментов локализуются по имени регистрации, например инструмент |
uploader | BlokUploader | undefined | Загрузчик уровня редактора для всех медиаассетов. Маршрутизация идёт по ТИПУ ассета, а не по инструменту. Сюда входят и ассеты, которыми инструмент владеет вне своего медиасемейства. Пример: обложка аудиоблока ( Загрузчик уровня инструмента ( |
media | MediaConfig | undefined | Преобразует каждую загруженную фотографию в несколько форматов. Затем редактор показывает лучший формат, который поддерживает браузер. Пока вы не перечислите форматы, функция выключена.
Каждый формат загружается отдельно. Ваш загрузчик получает в контексте
С видео всё иначе, потому что преобразование занимает время.
Blok не включает конвертер видео в сборку, потому что у него лицензия MPL-2.0. Установите |
server | string | undefined | Базовый URL сервиса, который говорит на контрактах загрузки и разворачивания ссылок Blok: например, Он подставляет Хранение документов он не настраивает. Ваши документы остаются вашими. См. |
ticket | string | undefined | Эндпоинт в ВАШЕМ приложении, который выпускает краткоживущий пропуск для вошедшего пользователя и отвечает Редактор кеширует пропуск и заменяет его заранее, до истечения срока, а не в момент истечения, поэтому ни один запрос не приходит уже недействительным. Загрузки и предпросмотр ссылок пользуются одним и тем же пропуском. |
persistence | { load(): Promise<OutputData | PersistedDocument | null>; save(data: OutputData, ctx: SaveContext): Promise<SaveResult | void>; onError?(error: unknown): void } | undefined | Загружает документ при монтировании и сохраняет его по мере изменений на ваш собственный эндпоинт. Сервис Blok документов не хранит. Здесь два колбэка, а не URL, потому что форма эндпоинта, его авторизация и id документа ваши. Сохранения никогда не идут параллельно, и за тем, что уже в полёте, следует только самый свежий ожидающий документ. Поэтому медленное сохранение, завершившееся после быстрого, не вернёт устаревшее содержимое. Загрузка происходит, только если вы не передали
|
collaboration | { doc: string; user?: { name: string; color?: string }; offline?: boolean; offlineScope?: string } | undefined | Совместное редактирование в реальном времени через сервис синхронизации, на который указывает
Задайте обе, и участники увидят «Автор последнего изменения: <имя>» на блоках, которые вы правили, потому что id уходит в состояние присутствия рядом с этим именем. Имя публикуется в комнату. Все в комнате также видят, когда вы последний раз проявляли активность, вместе с тем, где стоит ваш курсор. Если не задать ни то ни другое, вы присутствуете как анонимный аватар, и никакой id не публикуется.
Она ТРЕБУЕТ Это никогда не заявка на права доступа, ведь сервер этот id вообще не видит, и это никогда не отображаемая личность. Не выводите его из чего-либо, что меняется со временем: новое разделение при каждом обновлении страницы оставляет брошенными все прежние копии. Опция требует Когда опции нет, она ничего не стоит: Blok не открывает сокет. Только при монтировании: её изменение означает пересоздание редактора.
Состояние подключения и присутствующие люди приходят в событии |
documentId | string | undefined | Id этого документа в вашем приложении. Вкладки одного браузера с одинаковым Возьмите id, по которому приложение уже загружает и сохраняет документ, например параметр маршрута или ключ записи. Он должен быть уникальным на всём сайте. Пустая строка не принимается, и Без него Blok берёт Задаётся только при монтировании: чтобы показать другой документ, создайте редактор заново. Откуда взять id, рассказывает руководство «Синхронизация вкладок». |
tabSync | boolean | { settings?: boolean } | true | Синхронизация между вкладками одного браузера в реальном времени. Включена по умолчанию.
С |
theme | 'auto' | 'light' | 'dark' | 'auto' | Цветовая тема; 'auto' следует настройке ОС через prefers-color-scheme |
onThemeChange | (resolvedTheme: ResolvedTheme) => void | undefined | Срабатывает с ИТОГОВОЙ темой ('light' или 'dark') всякий раз, когда она меняется. Это и переключение настройки ОС, пока Это опция конфигурации ядра, а не проп только для адаптеров. Адаптеры фреймворков отдают тот же колбэк как проп |
link | { target?: string; rel?: string; transformHref?: (href: string) => string; transform?: (context: LinkTransformContext) => LinkTransformResult | void; unfurl?: { endpoint: string; headers?: Record<string, string> | (() => Promise<Record<string, string>>) } } | undefined | Управляет ссылками, которые создаёт Blok, вместо постобработки готового DOM. Действует на всех путях, порождающих
Обе функции обязаны быть идемпотентными: на путях рендера и вставки они повторно выполняются над уже преобразованными ссылками при каждом рендере.
|
linkPaste | { allowGenericEmbed?: boolean; allowedEmbedOrigins?: string[]; hostAliases?: string[] } | undefined | Поведение при вставке ссылки в стиле Notion. Задайте
Если вставить URL при выделенном тексте, выделенный текст станет ссылкой на него. Меню не открывается.
|
user | { id: string; name?: string } | undefined | Личность текущего редактирующего пользователя. Blok проставляет Добавьте Идентификаторы других людей Blok спрашивает у В совместной сессии идентификатор не приватен: он попадает в общий документ как |
resolveUser | (id: string) => UserInfo | Promise<UserInfo | null> | null | undefined | Определяет пользователя по идентификатору Blok спрашивает не больше одного раза на идентификатор: и ответ, и «такого не знаю» он держит всё время жизни редактора. Колбэк, который бросает исключение или отклоняет промис, поглощается и не ломает меню. Но ответом это не считается, поэтому про такой идентификатор спросят снова. Подключённый колбэк также означает, что неаутентифицированное имя из совместной сессии не показывается, пока идёт запрос. Идентификатор приходит обрезанным по краям и без NUL. Идентификатор длиннее 128 символов отклоняется и до колбэка не доходит. |
notifierPosition | NotifierPosition | 'bottom-center' | Где на экране закреплён контейнер встроенных тостов. |
notifier | (options: NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions) => void | undefined | Полностью заменяет встроенный тост. Blok вызывает ваш обработчик с тем же объектом опций вместо отрисовки собственного DOM-уведомления. |
logLevel | LogLevels | LogLevels.VERBOSE | Сколько Blok пишет в консоль. Значения: |