Skip to content

Текст и редактирование

Редактирование — возможность движка, а не сборка в приложении

Типичная слабость решений на canvas — накрыть canvas HTML-элементом input, когда нужен ввод. Из этого следует целая цепочка проблем: смещённая каретка, уехавшее окно кандидатов IME, рассинхронизированная прокрутка, сломанная доступность.

doper делает редактирование первоклассной возможностью ядра: каретка, выделение, выделение перетаскиванием, выбор слова двойным щелчком, навигация с клавиатуры, композиция IME, положение окна кандидатов, буфер обмена, отмена/повтор, режим только для чтения и пароль реализованы движком. Приложение не создаёт, не позиционирует и не синхронизирует ни одного HTML-элемента ввода.

Через виджеты

ts
import { TextField, TextArea } from "@dopejs/pingo";

TextField({
  value: order.note,
  revision: order.revision,
  semanticLabel: "Примечание к заказу",
  inputMode: "text",
  onTransaction: (transaction) => order.apply(transaction),
});

TextArea({ value: description, revision, rows: 4 });

Через примитив

ts
createElement("editableText", {
  value,
  revision,
  multiline: false,
  readOnly: false,
  password: false,
  maxGraphemes: 200,
  inputMode: "email",
  onTransaction: (transaction) => apply(transaction),
  onSubmit: () => moveToNextCell(),
});

Либо с локальным контроллером:

ts
import { useTextEditingController } from "@dopejs/pingo";

const editor = useTextEditingController({ value: cell.value });
createElement("editableText", { controller: editor });

Мост ввода и откат

Главный поток подключается к службе текстового ввода операционной системы по приоритету:

  1. EditContext — привязывается к canvas, получает текст, выделение и композицию и передаёт методу ввода control, selection и character bounds.
  2. Прокси ввода под управлением движка — если EditContext недоступен, хост держит ровно один глобальный скрытый textarea, который единообразно обрабатывает beforeinput, композицию, экранную клавиатуру и буфер обмена.

Второй пункт — это платформенная реализация отката, а не модель компонентов EmbedDOM: в Scene нет DOM, который соответствовал бы каждому редактируемому узлу один к одному. Оба пути проходят один и тот же набор контрактных тестов поведения.

Версионированные транзакции редактирования

Владение состоянием задано явно: оболочка владеет прикладными данными, ядро — мгновенным состоянием активной сессии редактирования.

ввод → ядро проверяет base_revision → сразу применяет и перерисовывает → отправляет назад версионированную EditTransaction

                                     приложение подтверждает или присылает исправленное значение с новой ревизией

Устаревшая транзакция никогда не перезапишет более новое состояние. Это значит, что каждое нажатие клавиши не требует полного прогона сборки TSX, и при этом контролируемые данные и прикладная валидация продолжают работать.

ts
onTransaction: (transaction) => {
  // transaction.baseRevision / revision / delta / selection / kind
  value = applyDelta(value, transaction);
};

Модель позиций в тексте

Веб-API ввода оперируют смещениями UTF-16, строки в Rust — это UTF-8, а границы графем, кластеров шейпинга и видимых глифов различаются между собой. Движок ведёт явное соответствие:

смещение UTF-16 ↔ скаляр Unicode ↔ графема ↔ кластер шейпинга ↔ глиф / строка

На границе протокола всюду используется UTF-16, чтобы совпадать с EditContext и InputEvent. Удаление, перемещение и выделение никогда не разрезают графему, комбинирующую последовательность, эмодзи с ZWJ или кластер шейпинга — это защищено property-тестами и матрицей фикстур композиции (комбинирующие символы, эмодзи ZWJ, RTL, многосегментные кандидаты CJK).

Пароли и приватность

Текст пароля не попадает ни в запись и воспроизведение, ни в логи, ни в открытый текст devtools, ни в значения доступности; цель-пароль также не пишет в буфер обмена. Ядро выдаёт только маскированные глифы, поэтому открытый текст вообще не доходит до DisplayList. Это подтверждено автоматическими тестами, и вы можете сами проверить DOM в опубликованном Playground.

Известные границы

  • Визуальная навигация в bidi появится вместе с поддержкой bidi-текста; сейчас это явно отложенный пункт.
  • Схема форматированного текста, разрешение конфликтов при совместной работе, формулы и команды Markdown относятся к верхним слоям, но могут строиться на тех же транзакциях редактирования и том же API выделения.

Опубликовано под лицензией MIT