---
title: "Tools API — регистрация и обновление инструментов"
description: "Регистрация инструментов, обновление тулбокса на лету и чтение списка доступных в данный момент инструментов."
source: https://blokeditor.com/ru/docs/tools-api/
lastmod: 2026-09-07
---

Фреймворк JavaScript

Расширение и система Инструменты

На этой странице tools.getBlockTools()

# Tools API: регистрация и обновление инструментов

Доступ и управление инструментами редактора.

[Редактировать на GitHub](https://github.com/JackUait/blok/blob/main/docs/src/components/api/api-data.ts)

### Как получить экземпляр редактора

Методы ниже вызываются на редакторе, созданном через new Blok(). Они доступны после того, как разрешится editor.isReady.

TypeScript

```
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;

// Call any API method on it.
editor.caret.setToLastBlock('end');
```

## Методы

### tools.getBlockTools()

BlockToolAdapter[]

Получить все доступные адаптеры блочных инструментов. Каждый адаптер предоставляет `name` и метаданные инструмента, включая `assetKind` — он равен `'image' | 'video' | 'audio' | 'file'` у медиаинструментов, которые хранят URL загруженного ресурса в `data.url`, и `undefined` у остальных. Используйте это, чтобы во время выполнения определить набор инструментов, несущих медиа (вместо жёсткого прописывания формы данных каждого инструмента), и сверить `data.url` сохранённого документа с вашим CDN — например, чтобы подчистить осиротевшие загрузки.

Когда использовать

Перечисляет зарегистрированные блочные инструменты во время выполнения — удобно для своего выбора блоков или отладки конфигурации.

TypeScript

```
const blockTools = editor.tools.getBlockTools();
blockTools.forEach(tool => {
  console.log('Available tool:', tool.name);
});

// Discover which block types hold uploaded media, then collect their URLs
const mediaTypes = new Set(
  editor.tools.getBlockTools().filter(t => t.assetKind).map(t => t.name)
);
const referenced = (await editor.save()).blocks
  .filter(b => mediaTypes.has(b.type))
  .map(b => b.data.url);
```

### tools.getToolsConfig()

ToolsConfig

Возвращает связанную с инструментами конфигурацию этого экземпляра — { tools, inlineToolbar?, tunes?, theme? } — для создания вложенных редакторов Blok с тем же набором инструментов.

TypeScript

```
const nested = new Blok({ holder, ...editor.tools.getToolsConfig() });
```

### tools.update(name, config)

void

Поверхностно объединить новую конфигурацию с установленным инструментом во время выполнения — без пересоздания редактора. Ключ `toolbox` трактуется как настройка уровня инструмента (та же, что `toolbox` в карте `tools`): передайте `toolbox: false`, чтобы скрыть инструмент со всех поверхностей вставки (существующие блоки продолжают отрисовываться), или объект toolbox, чтобы (снова) показать его — разграничение прав без пересборки редактора. В React-адаптере это происходит автоматически: измените значение `toolbox` в пропе `tools`, и `useBlok`/`BlokEditor` применит его на месте.

TypeScript

```
// Swap a config value (e.g. an uploader) at runtime
editor.tools.update('image', { uploader: { uploadByFile } });

// Permission flip: hide the tool from the + / slash / convert menus.
// Existing goodsList blocks still render; insertion is gated.
editor.tools.update('goodsList', { toolbox: false });

// Re-enable it later
editor.tools.update('goodsList', { toolbox: { title: 'Goods List' } });
```

### tools.setInlineToolbar(config)

void

Runtime-сеттер глобальной опции `inlineToolbar`. Заново назначает строчные инструменты каждому блочному инструменту и пересобирает мемоизированные конфигурации санитизации — санитизация при вставке следует новому набору сразу, а строчная панель отражает его при следующем выделении. Настройки `inlineToolbar` на уровне инструмента (массивы и отказы) остаются приоритетными. Передайте `true` для всех строчных инструментов, `false` — чтобы отключить, или упорядоченный список имён строчных инструментов. Если вы отображаете сохранённый контент через @bloklabs/core/view, учтите: viewSchema собирается из того значения inlineToolbar, с которым она была определена — после рантайм-вызова setInlineToolbar с пользовательскими строчными инструментами пересоберите её через defineBlokSchema, прежде чем вызывать blocksToHtml.

Когда использовать

Вступает в силу при следующем выделении; санитизация при вставке пересобирается сразу, так что вставляемый контент следует новому набору строчных инструментов немедленно. Если сохранённый контент отображается через @bloklabs/core/view, учтите: viewSchema составляется из того значения inlineToolbar, с которым была определена — после runtime-вызова setInlineToolbar с пользовательскими строчными инструментами пересоберите её через defineBlokSchema перед вызовом blocksToHtml.

Параметры

| Параметр | Тип | Обязательный | По умолчанию | Описание |
| --- | --- | --- | --- | --- |
| `config` | `boolean | string[]` | Обязательный | — | `true` включает все зарегистрированные строчные инструменты, `false` отключает строчную панель, массив ограничивает её перечисленными инструментами в этом порядке. |

TypeScript

```
// Restrict inline formatting to bold and italic at runtime
editor.tools.setInlineToolbar(['bold', 'italic']);

// Disable the inline toolbar entirely
editor.tools.setInlineToolbar(false);

// Back to every registered inline tool
editor.tools.setInlineToolbar(true);
```

### tools.isInstalled(name)

boolean

Возвращает true, если инструмент с указанным именем установлен и доступен в этом экземпляре редактора — блочный, строчный или тюн. Публичная интроспекция набора установленных инструментов, например как проверка перед `tools.update(name, config)`, который бросает исключение для неизвестных имён.

Когда использовать

Защищает runtime-вызовы инструментов — `tools.update()` бросает исключение для неустановленных имён.

Параметры

| Параметр | Тип | Обязательный | По умолчанию | Описание |
| --- | --- | --- | --- | --- |
| `name` | `string` | Обязательный | — | Имя зарегистрированного инструмента для проверки. |

TypeScript

```
if (editor.tools.isInstalled('image')) {
  editor.tools.update('image', { uploader: { uploadByFile } });
}
```

### defineTool(toolClass, settings?)

ExternalToolSettings

Вспомогательная функция регистрации, экспортируемая из `@bloklabs/core/tools`, а не член пространства имён `tools`. Обычная карта `tools` типизирует каждую запись голым `ToolSettings`, у которого `Config` откатывается к `Record<string, unknown>`, поэтому опечатка в ключе конфигурации (`defaultLevle` вместо `defaultLevel`) компилируется молча. `defineTool` восстанавливает настоящий тип конфигурации инструмента из его конструктора и применяет его к `settings.config`, превращая такие опечатки в ошибки компиляции. Проверка типов происходит на аргументе `settings`; ВОЗВРАЩАЕМЫЙ тип остаётся стёртым `ExternalToolSettings`, поэтому результат ложится прямо в карту `tools`. `ExtractToolConfig<TClass>` — тип, который выполняет это восстановление, — экспортируется рядом с ней и откатывается к `Record<string, unknown>` для классов инструментов, чей конструктор не объявляет конкретной конфигурации.

TypeScript

```
import { Blok } from '@bloklabs/core';
import { Header, defineTool } from '@bloklabs/core/tools';

new Blok({
  tools: {
    header: defineTool(Header, { config: { levels: [1, 2, 3] } }),
    // `defaultLevle: 2` here would now be a compile error
  },
});
```

### mountChildBlocks(container, children)

void

Согласователь холдеров детей для блоков-контейнеров, экспортируемый из `@bloklabs/core/tools`. Вызывайте его из хука `rendered()` вашего инструмента — именно его используют встроенные инструменты тоггла, выноски и колонки, и именно его запускают React/Vue/Angular-адаптеры блоков на каждом коммите. Он идемпотентен и дёшев, поэтому запускайте его на каждой отрисовке. По каждому ребёнку он: оставляет как есть холдер, уже находящийся внутри `container`; ЗАБИРАЕТ холдер, застрявший во вложенном контейнере, который ОХВАТЫВАЕТ `container`, и вставляет его на позицию из модели, а не добавляет в конец; оставляет как есть холдеры, лежащие в любом ДРУГОМ вложенном контейнере, поэтому два контейнера никогда не смогут украсть блоки друг у друга; и монтирует всё остальное на позицию из модели. Именно этот возврат позволяет контейнеру пережить порядок вставки: ядро привязывает только что вставленного первого ребёнка как DOM-соседа блока-контейнера, поэтому без него ребёнок вложенного контейнера отрисовывается на один уровень выше — навсегда, если слот для детей вашего контейнера ещё не был создан к моменту вставки (портал фреймворка коммитит отрисовку после вставки в ядре). Пометьте `container` атрибутом `data-blok-nested-blocks`, чтобы остальной редактор распознавал его как контейнер.

Параметры

| Параметр | Тип | Обязательный | По умолчанию | Описание |
| --- | --- | --- | --- | --- |
| `container` | `HTMLElement` | Обязательный | — | Элемент, в котором должны находиться холдеры детей, — элемент, несущий `data-blok-nested-blocks`. |
| `children` | `{ holder: HTMLElement }[]` | Обязательный | — | Дети блока в порядке модели, обычно `api.blocks.getChildren(blockId)`. |

TypeScript

```
import { mountChildBlocks } from '@bloklabs/core/tools';

class CardTool {
  constructor({ api, block }) {
    this.api = api;
    this.blockId = block.id;
  }

  render() {
    this.slot = document.createElement('div');
    this.slot.setAttribute('data-blok-nested-blocks', '');

    return this.slot;
  }

  // Runs after the holder is in the document, and on every re-render
  rendered() {
    mountChildBlocks(this.slot, this.api.blocks.getChildren(this.blockId));
  }
}
```

### BlockToolConstructorOptions.origin

BlockOrigin

Сигнал «создание или восстановление» в контракте инструмента, передаваемый в конструктор каждого блочного инструмента рядом с `data`, `block` и `readOnly`. Контейнерный инструмент, который засевает детей по умолчанию — строку из двух колонок, карточку, начинающуюся с заголовка, — может делать это только один раз, при создании. Во всех остальных случаях к моменту конструирования инструмента документ уже говорит, какие у него дети, а при восстановлении эти дети обычно приходят на тик ПОЗЖЕ вызова `rendered()`, поэтому пустой `api.blocks.getChildren()` там лишь временный: засев по такому чтению создаёт фантомных детей рядом с настоящими. Значения СОЗДАНИЯ — засевать можно: `'user'` (прямой жест редактирования: Enter, кнопка «плюс», тулбокс по «/», настройки блока, Markdown-сокращение), `'api'` (программные `blocks.insert` / `insertMany` / `insertInsideParent`), `'convert'` (преобразование блока). Значения ВОССТАНОВЛЕНИЯ — засевать нельзя: `'load'` (отрисовка документа), `'replay'` (воспроизведение отмены/повтора или удалённое совместное изменение), `'paste'` (вставленное содержимое, которое приносит своих детей), `'probe'` (экземпляр ВНЕ ДЕРЕВА, который `blocks.composeBlockData()` создаёт, чтобы прочитать данные инструмента по умолчанию — он никогда не вставляется, но всё равно выполняет `render()` и `rendered()`, поэтому он вообще не должен трогать дерево блоков). Blok всегда передаёт его; отсутствующее значение трактуйте как `'api'`, а проверку пишите как белый список значений создания, чтобы будущее значение origin по умолчанию не считалось созданием. Сочетайте его с `blocks.insert(..., origin)`, если вы запускаете вставку из собственного интерфейса. В React/Vue/Angular-адаптерах вы редко читаете его вручную: хук `onCreated` в спецификации блока уже кодирует этот белый список и срабатывает после первого коммита адаптера — в тот тик, когда DOM блока и принятые им холдеры детей действительно существуют.

TypeScript

```
class TwoColumnCard {
  constructor({ api, block, origin }) {
    this.api = api;
    this.blockId = block.id;
    // Allow-list, so a future origin never silently opts into seeding.
    this.isCreation = ['user', 'api', 'convert', undefined].includes(origin);
  }

  render() {
    this.slot = document.createElement('div');
    this.slot.setAttribute('data-blok-nested-blocks', '');

    return this.slot;
  }

  rendered() {
    const children = this.api.blocks.getChildren(this.blockId);

    if (children.length > 0) {
      mountChildBlocks(this.slot, children);

      return;
    }

    // Empty on a load / undo-redo replay / paste / probe means "my children
    // have not arrived yet", NOT "I am brand new". Only a creation may seed.
    if (!this.isCreation) {
      return;
    }

    this.seedColumns();
  }
}
```

### BlockToolConstructable.keepsChildrenOnEnter

boolean

Статическое поле на КЛАССЕ вашего инструмента, которое решает, куда уходит Enter на пустом ПОСЛЕДНЕМ ребёнке контейнера. По умолчанию Blok читает эту пустую последнюю строку как способ автора выйти наружу: при наличии соседей строка выносится к родителю самого контейнера, а если она единственный ребёнок — после всего контейнера вставляется новый блок, как ведёт себя выноска в Notion. Контейнеру для вёрстки, чьи дети И ЕСТЬ его содержимое (колонка, карточка, блок `steps`), нужно обратное, и без этого объявления выход оставляет новую строку рядом с контейнером. Задайте `true` — и новая строка остаётся внутри, по тому же правилу, которому следуют встроенные `column`, `column_list` и `toggle`. Вывести это из DOM нельзя: выноска отрисовывает ровно тот же слот `data-blok-nested-blocks`, что и колонка, и намеренно сохраняет выход, поэтому это политика уровня инструмента. Ядро читает его и для симметричного жеста «убрать один уровень отступа» (Enter/Backspace на блоке, вложенном в ОБЫЧНОГО родителя), поэтому объявивший инструмент и там считается контейнером, а его дети никогда не выходят из него по одному уровню отступа. В React/Vue/Angular-адаптерах объявляйте его в наборе `statics` спецификации блока, как любое другое статическое поле класса.

TypeScript

```
class StepsTool {
  static keepsChildrenOnEnter = true;

  render() {
    this.slot = document.createElement('div');
    this.slot.setAttribute('data-blok-nested-blocks', '');

    return this.slot;
  }

  rendered() {
    mountChildBlocks(this.slot, this.api.blocks.getChildren(this.blockId));
  }
}

// Framework adapters forward it through `statics`:
export const StepsTool = createReactBlock({
  type: 'steps',
  statics: { ownsChildren: true, keepsChildrenOnEnter: true },
  component: StepsCard,
});
```

### BlockToolConstructable.childTools

{ allow?: string[]; deny?: string[] }

Статическое поле на КЛАССЕ вашего инструмента, объявляющее, какие блочные инструменты могут быть ПРЯМЫМИ детьми его блока, — и ядро само следит за этим везде. При ВСТАВКЕ запрещённый инструмент понижается (никогда не отклоняется, потому что Enter обязан всегда порождать блок): целью становится первая запись `allow`, поэтому с `allow: ['segment-item']` «Enter в конце сегмента» порождает ещё один сегмент, а не случайный параграф. При ПЕРЕМЕЩЕНИИ перетаскивание или переупорядочивание с клавиатуры, которое перенесло бы запрещённый блок через границу контейнера, отклоняется. В ТУЛБОКСЕ запрещённые инструменты скрыты, пока курсор находится в ребёнке. `deny` побеждает `allow` для инструмента, названного в обоих, а пустые списки читаются как «нет ограничений». Это избирательный, учитывающий вставку аналог `ownsChildren`, который работает по принципу «всё или ничего» и ограничивает только перемещения, — и обобщённая форма `restrictedTools` из инструмента таблицы, чьё применение жёстко привязано к ячейкам таблицы. Без него контейнерному инструменту приходится защищаться ниже по течению: фильтровать `child.name` при отрисовке, делать свой CSS устойчивым к чужому ребёнку и вычищать залётные блоки из сохранённых документов. В React/Vue/Angular-адаптерах объявляйте его в наборе `statics` спецификации блока, как любое другое статическое поле класса.

TypeScript

```
class Segments {
  static get childTools() {
    return { allow: ['segment-item'] };
  }

  render() {
    this.slot = document.createElement('div');
    this.slot.setAttribute('data-blok-nested-blocks', '');

    return this.slot;
  }
}

// Only forbid a few tools, accept everything else
class Callout {
  static childTools = { deny: ['table', 'column_list'] };
}

// Framework adapters forward it through `statics`:
export const Segments = createReactBlock({
  type: 'segments',
  statics: { childTools: { allow: ['segment-item'] } },
  component: SegmentsCard,
});
```

### setData(newData)

boolean | void | Promise<boolean | void>

Необязательный метод вашего инструмента, который применяет новые данные к ЖИВОМУ экземпляру. Объявите его — и `blocks.update()`, отмена/повтор и удалённые совместные правки станут переиспользовать уже отрисованный блок вместо того, чтобы собирать его заново: ни нового экземпляра инструмента, ни нового холдера, поэтому эфемерное состояние (открытое меню, позиция прокрутки, локальное состояние компонента фреймворка), принятые холдеры детей и курсор сохраняются. Без него ядро уничтожает блок и строит замену — именно поэтому хост, вызывавший `blocks.update()` на каждое нажатие клавиши, раньше наблюдал, как блок на компоненте становится пустым. Возвращайте `false`, когда применить данные на месте нельзя — так делает инструмент списка при смене стиля, которой нужна другая форма DOM, — и ядро откатывается к полной пересборке; `true` или ничего означает, что данные применены. Исключение действует так же, как `false` (логируется, затем пересборка). React/Vue/Angular-фабрики блоков реализуют его за вас, поэтому блоки адаптеров автоматически идут по пути обновления на месте.

Параметры

| Параметр | Тип | Обязательный | По умолчанию | Описание |
| --- | --- | --- | --- | --- |
| `newData` | `BlockToolData` | Обязательный | — | Полные данные блока после обновления — существующие данные, объединённые с патчем вызывающей стороны, а не только сам патч. |

TypeScript

```
class CalloutTool {
  setData(newData) {
    if (newData.variant !== this.data.variant) {
      // A different variant renders a different DOM shape — let core rebuild.
      return false;
    }

    this.data = newData;
    this.box.textContent = newData.text ?? '';

    return true;
  }
}
```
